From: Julian Seward Date: Fri, 7 Jan 2005 12:09:15 +0000 (+0000) Subject: Add a trivial new IR construction: a memory fence statement. Connect X-Git-Tag: svn/VALGRIND_3_0_1^2~637 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4ea7cb7702feda2ae15573be6edb912227c9f2a9;p=thirdparty%2Fvalgrind.git Add a trivial new IR construction: a memory fence statement. Connect up to x86 front and back ends. git-svn-id: svn://svn.valgrind.org/vex/trunk@697 --- diff --git a/VEX/priv/guest-x86/toIR.c b/VEX/priv/guest-x86/toIR.c index cd58b500f0..48a10f8a49 100644 --- a/VEX/priv/guest-x86/toIR.c +++ b/VEX/priv/guest-x86/toIR.c @@ -7842,7 +7842,9 @@ DisResult disInstr ( /*IN*/ Bool resteerOK, && epartIsReg(insn[2]) && gregOfRM(insn[2]) == 7) { vassert(sz == 4); delta += 3; - /* nothing to do */ + /* Insert a memory fence. It's sometimes important that these + are carried through to the generated code. */ + stmt( IRStmt_MFence() ); DIP("sfence\n"); goto decode_success; } @@ -8618,7 +8620,9 @@ DisResult disInstr ( /*IN*/ Bool resteerOK, && (gregOfRM(insn[2]) == 5 || gregOfRM(insn[2]) == 6)) { vassert(sz == 4); delta += 3; - /* nothing to do */ + /* Insert a memory fence. It's sometimes important that these + are carried through to the generated code. */ + stmt( IRStmt_MFence() ); DIP("%sfence\n", gregOfRM(insn[2])==5 ? "l" : "m"); goto decode_success; } diff --git a/VEX/priv/host-x86/hdefs.c b/VEX/priv/host-x86/hdefs.c index 2129e42160..c254d17a38 100644 --- a/VEX/priv/host-x86/hdefs.c +++ b/VEX/priv/host-x86/hdefs.c @@ -704,6 +704,17 @@ X86Instr* X86Instr_Bsfr32 ( Bool isFwds, HReg src, HReg dst ) { i->Xin.Bsfr32.dst = dst; return i; } +X86Instr* X86Instr_MFence ( VexSubArch subarch ) +{ + X86Instr* i = LibVEX_Alloc(sizeof(X86Instr)); + i->tag = Xin_MFence; + i->Xin.MFence.subarch = subarch; + vassert(subarch == VexSubArchX86_sse0 + || subarch == VexSubArchX86_sse1 + || subarch == VexSubArchX86_sse2); + return i; +} + X86Instr* X86Instr_FpUnary ( X86FpOp op, HReg src, HReg dst ) { X86Instr* i = LibVEX_Alloc(sizeof(X86Instr)); i->tag = Xin_FpUnary; @@ -980,6 +991,10 @@ void ppX86Instr ( X86Instr* i ) { vex_printf(","); ppHRegX86(i->Xin.Bsfr32.dst); return; + case Xin_MFence: + vex_printf("mfence(%s)", + LibVEX_ppVexSubArch(i->Xin.MFence.subarch)); + return; case Xin_FpUnary: vex_printf("g%sD ", showX86FpOp(i->Xin.FpUnary.op)); ppHRegX86(i->Xin.FpUnary.src); @@ -1230,6 +1245,8 @@ void getRegUsage_X86Instr (HRegUsage* u, X86Instr* i) addHRegUse(u, HRmRead, i->Xin.Bsfr32.src); addHRegUse(u, HRmWrite, i->Xin.Bsfr32.dst); return; + case Xin_MFence: + return; case Xin_FpUnary: addHRegUse(u, HRmRead, i->Xin.FpUnary.src); addHRegUse(u, HRmWrite, i->Xin.FpUnary.dst); @@ -1408,6 +1425,8 @@ void mapRegs_X86Instr (HRegRemap* m, X86Instr* i) mapReg(m, &i->Xin.Bsfr32.src); mapReg(m, &i->Xin.Bsfr32.dst); return; + case Xin_MFence: + return; case Xin_FpUnary: mapReg(m, &i->Xin.FpUnary.src); mapReg(m, &i->Xin.FpUnary.dst); @@ -2317,6 +2336,33 @@ Int emit_X86Instr ( UChar* buf, Int nbuf, X86Instr* i ) p = doAMode_R(p, i->Xin.Bsfr32.dst, i->Xin.Bsfr32.src); goto done; + case Xin_MFence: + /* see comment in hdefs.h re this insn */ +vex_printf("EMIT FENCE\n"); + switch (i->Xin.MFence.subarch) { + case VexSubArchX86_sse0: + vassert(0); /* awaiting test case */ + /* lock addl $0,0(%esp) */ + *p++ = 0xF0; *p++ = 0x83; *p++ = 0x44; + *p++ = 0x24; *p++ = 0x00; *p++ = 0x00; + goto done; + case VexSubArchX86_sse1: + /* sfence */ + *p++ = 0x0F; *p++ = 0xAE; *p++ = 0xF8; + /* lock addl $0,0(%esp) */ + *p++ = 0xF0; *p++ = 0x83; *p++ = 0x44; + *p++ = 0x24; *p++ = 0x00; *p++ = 0x00; + goto done; + case VexSubArchX86_sse2: + vassert(0); /* awaiting test case */ + /* mfence */ + *p++ = 0x0F; *p++ = 0xAE; *p++ = 0xF0; + goto done; + default: + vpanic("emit_X86Instr:mfence:subarch"); + } + break; + case Xin_Store: if (i->Xin.Store.sz == 2) { /* This case, at least, is simple, given that we can diff --git a/VEX/priv/host-x86/hdefs.h b/VEX/priv/host-x86/hdefs.h index 7c550f9b76..f8e74a7705 100644 --- a/VEX/priv/host-x86/hdefs.h +++ b/VEX/priv/host-x86/hdefs.h @@ -365,6 +365,7 @@ typedef Xin_Store, /* store 16/8 bit value in memory */ Xin_Set32, /* convert condition code to 32-bit value */ Xin_Bsfr32, /* 32-bit bsf/bsr */ + Xin_MFence, /* mem fence (not just sse2, but sse0 and 1 too) */ Xin_FpUnary, /* FP fake unary op */ Xin_FpBinary, /* FP fake binary op */ @@ -490,6 +491,16 @@ typedef HReg src; HReg dst; } Bsfr32; + /* Mem fence (not just sse2, but sse0 and 1 too). In short, + an insn which flushes all preceding loads and stores as + much as possible before continuing. On SSE2 we emit a + real "mfence", on SSE1 "sfence ; lock addl $0,0(%esp)" and + on SSE0 "lock addl $0,0(%esp)". This insn therefore + carries the subarch so the assembler knows what to + emit. */ + struct { + VexSubArch subarch; + } MFence; /* X86 Floating point (fake 3-operand, "flat reg file" insns) */ struct { @@ -624,6 +635,7 @@ extern X86Instr* X86Instr_LoadEX ( UChar szSmall, Bool syned, extern X86Instr* X86Instr_Store ( UChar sz, HReg src, X86AMode* dst ); extern X86Instr* X86Instr_Set32 ( X86CondCode cond, HReg dst ); extern X86Instr* X86Instr_Bsfr32 ( Bool isFwds, HReg src, HReg dst ); +extern X86Instr* X86Instr_MFence ( VexSubArch ); extern X86Instr* X86Instr_FpUnary ( X86FpOp op, HReg src, HReg dst ); extern X86Instr* X86Instr_FpBinary ( X86FpOp op, HReg srcL, HReg srcR, HReg dst ); diff --git a/VEX/priv/host-x86/isel.c b/VEX/priv/host-x86/isel.c index f667a5930f..71a3fc7021 100644 --- a/VEX/priv/host-x86/isel.c +++ b/VEX/priv/host-x86/isel.c @@ -3098,6 +3098,11 @@ static void iselStmt ( ISelEnv* env, IRStmt* stmt ) break; } + /* --------- MEM FENCE --------- */ + case Ist_MFence: + addInstr(env, X86Instr_MFence(env->subarch)); + return; + /* --------- EXIT --------- */ case Ist_Exit: { X86RI* dst; diff --git a/VEX/priv/ir/irdefs.c b/VEX/priv/ir/irdefs.c index a1d9dc8888..1e01332e11 100644 --- a/VEX/priv/ir/irdefs.c +++ b/VEX/priv/ir/irdefs.c @@ -46,19 +46,19 @@ void ppIRType ( IRType ty ) { - switch (ty) { - case Ity_INVALID: vex_printf("Ity_INVALID"); break; - case Ity_I1: vex_printf( "I1"); break; - case Ity_I8: vex_printf( "I8"); break; - case Ity_I16: vex_printf( "I16"); break; - case Ity_I32: vex_printf( "I32"); break; - case Ity_I64: vex_printf( "I64"); break; - case Ity_F32: vex_printf( "F32"); break; - case Ity_F64: vex_printf( "F64"); break; - case Ity_V128: vex_printf( "V128"); break; - default: vex_printf("ty = 0x%x\n", (Int)ty); - vpanic("ppIRType"); - } + switch (ty) { + case Ity_INVALID: vex_printf("Ity_INVALID"); break; + case Ity_I1: vex_printf( "I1"); break; + case Ity_I8: vex_printf( "I8"); break; + case Ity_I16: vex_printf( "I16"); break; + case Ity_I32: vex_printf( "I32"); break; + case Ity_I64: vex_printf( "I64"); break; + case Ity_F32: vex_printf( "F32"); break; + case Ity_F64: vex_printf( "F64"); break; + case Ity_V128: vex_printf( "V128"); break; + default: vex_printf("ty = 0x%x\n", (Int)ty); + vpanic("ppIRType"); + } } void ppIRConst ( IRConst* con ) @@ -539,6 +539,9 @@ void ppIRStmt ( IRStmt* s ) case Ist_Dirty: ppIRDirty(s->Ist.Dirty.details); break; + case Ist_MFence: + vex_printf("IRMemoryFence"); + break; case Ist_Exit: vex_printf( "if (" ); ppIRExpr(s->Ist.Exit.guard); @@ -871,6 +874,12 @@ IRStmt* IRStmt_Dirty ( IRDirty* d ) s->Ist.Dirty.details = d; return s; } +IRStmt* IRStmt_MFence ( void ) +{ + IRStmt* s = LibVEX_Alloc(sizeof(IRStmt)); + s->tag = Ist_MFence; + return s; +} IRStmt* IRStmt_Exit ( IRExpr* guard, IRJumpKind jk, IRConst* dst ) { IRStmt* s = LibVEX_Alloc(sizeof(IRStmt)); s->tag = Ist_Exit; @@ -1495,6 +1504,8 @@ Bool isFlatIRStmt ( IRStmt* st ) if (di->mAddr && !isAtom(di->mAddr)) return False; return True; + case Ist_MFence: + return True; case Ist_Exit: return isAtom(st->Ist.Exit.guard); default: @@ -1651,6 +1662,8 @@ void useBeforeDef_Stmt ( IRBB* bb, IRStmt* stmt, Int* def_counts ) if (d->mFx != Ifx_None) useBeforeDef_Expr(bb,stmt,d->mAddr,def_counts); break; + case Ist_MFence: + break; case Ist_Exit: useBeforeDef_Expr(bb,stmt,stmt->Ist.Exit.guard,def_counts); break; @@ -1838,7 +1851,8 @@ void tcStmt ( IRBB* bb, IRStmt* stmt, IRType gWordTy ) break; bad_dirty: sanityCheckFail(bb,stmt,"IRStmt.Dirty: ill-formed"); - + case Ist_MFence: + break; case Ist_Exit: tcExpr( bb, stmt, stmt->Ist.Exit.guard, gWordTy ); if (typeOfIRExpr(tyenv,stmt->Ist.Exit.guard) != Ity_I1) diff --git a/VEX/priv/ir/iropt.c b/VEX/priv/ir/iropt.c index 7570b76765..50dd02dcae 100644 --- a/VEX/priv/ir/iropt.c +++ b/VEX/priv/ir/iropt.c @@ -408,6 +408,9 @@ static void flatten_Stmt ( IRBB* bb, IRStmt* st ) d2->args[i] = flatten_Expr(bb, d2->args[i]); addStmtToIRBB(bb, IRStmt_Dirty(d2)); break; + case Ist_MFence: + addStmtToIRBB(bb, st); + break; case Ist_Exit: e1 = flatten_Expr(bb, st->Ist.Exit.guard); addStmtToIRBB(bb, IRStmt_Exit(e1, st->Ist.Exit.jk, @@ -664,6 +667,9 @@ static void handle_gets_Stmt ( state requiring precise exceptions needs to be flushed. The crude solution is just to flush everything; we could easily enough do a lot better if needed. */ + /* Probably also overly-conservative, but also dump everything + if we hit a memory fence. */ + case Ist_MFence: case Ist_Dirty: for (j = 0; j < env->used; j++) env->inuse[j] = False; @@ -1320,6 +1326,9 @@ static IRStmt* subst_and_fold_Stmt ( IRExpr** env, IRStmt* st ) return IRStmt_Dirty(d2); } + case Ist_MFence: + return IRStmt_MFence(); + case Ist_Exit: { IRExpr* fcond; vassert(isAtom(st->Ist.Exit.guard)); @@ -1505,6 +1514,8 @@ static void addUses_Stmt ( Bool* set, IRStmt* st ) for (i = 0; d->args[i] != NULL; i++) addUses_Expr(set, d->args[i]); return; + case Ist_MFence: + return; case Ist_Exit: addUses_Expr(set, st->Ist.Exit.guard); return; @@ -2932,6 +2943,8 @@ static void occCount_Stmt ( TmpInfo** env, IRStmt* st ) for (i = 0; d->args[i]; i++) occCount_Expr(env, d->args[i]); return; + case Ist_MFence: + return; case Ist_Exit: occCount_Expr(env, st->Ist.Exit.guard); return; @@ -3062,6 +3075,8 @@ static IRStmt* tbSubst_Stmt ( TmpInfo** env, IRStmt* st ) st->Ist.Exit.jk, st->Ist.Exit.dst ); + case Ist_MFence: + return IRStmt_MFence(); case Ist_Dirty: d = st->Ist.Dirty.details; d2 = emptyIRDirty(); @@ -3282,8 +3297,11 @@ static void dumpInvalidated ( TmpInfo** env, IRBB* bb, /*INOUT*/Int* j ) appeared. (Stupid algorithm): first, mark all bindings which need to be dumped. Then, dump them in the order in which they were defined. */ + invPut = st->tag == Ist_Put - || st->tag == Ist_PutI || st->tag == Ist_Dirty; + || st->tag == Ist_PutI + || st->tag == Ist_Dirty; + invStore = st->tag == Ist_STle || st->tag == Ist_Dirty; @@ -3294,7 +3312,8 @@ static void dumpInvalidated ( TmpInfo** env, IRBB* bb, /*INOUT*/Int* j ) if (!ti->expr) continue; - /* We have to invalidate this binding. */ + /* Do we have to invalidate this binding? */ + ti->invalidateMe = /* a store invalidates loaded data */ (ti->eDoesLoad && invStore) @@ -3305,11 +3324,16 @@ static void dumpInvalidated ( TmpInfo** env, IRBB* bb, /*INOUT*/Int* j ) invalidate trees containing loads if the Put in question is marked as requiring precise exceptions. */ - || (ti->eDoesLoad && invPut); + || (ti->eDoesLoad && invPut) + /* probably overly conservative: a memory fence + invalidates absolutely everything, so that all + computation prior to it is forced to complete before + proceeding with the fence. */ + || st->tag == Ist_MFence; /* if (ti->invalidateMe) - vex_printf("SET INVAL\n"); - */ + vex_printf("SET INVAL\n"); + */ } dumpInvalidated ( env, bb, &j ); @@ -3432,9 +3456,6 @@ static Bool hasGetIorPutI ( IRBB* bb ) vassert(isAtom(st->Ist.STle.addr)); vassert(isAtom(st->Ist.STle.data)); break; - case Ist_Exit: - vassert(isAtom(st->Ist.Exit.guard)); - break; case Ist_Dirty: d = st->Ist.Dirty.details; vassert(isAtom(d->guard)); @@ -3443,6 +3464,11 @@ static Bool hasGetIorPutI ( IRBB* bb ) if (d->mFx != Ifx_None) vassert(isAtom(d->mAddr)); break; + case Ist_MFence: + break; + case Ist_Exit: + vassert(isAtom(st->Ist.Exit.guard)); + break; default: ppIRStmt(st); vpanic("hasGetIorPutI"); diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index 026b5fd318..6e41b42964 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -771,6 +771,7 @@ typedef Ist_Tmp, /* assign value to temporary */ Ist_STle, /* little-endian write to memory */ Ist_Dirty, /* call complex ("dirty") helper function */ + Ist_MFence, /* memory fence */ Ist_Exit /* conditional exit from BB */ } IRStmtTag; @@ -800,6 +801,8 @@ typedef struct { IRDirty* details; } Dirty; + struct { + } MFence; struct { IRExpr* guard; IRJumpKind jk; @@ -809,13 +812,14 @@ typedef } IRStmt; -extern IRStmt* IRStmt_Put ( Int off, IRExpr* data ); -extern IRStmt* IRStmt_PutI ( IRArray* descr, IRExpr* ix, Int bias, - IRExpr* data ); -extern IRStmt* IRStmt_Tmp ( IRTemp tmp, IRExpr* data ); -extern IRStmt* IRStmt_STle ( IRExpr* addr, IRExpr* data ); -extern IRStmt* IRStmt_Dirty ( IRDirty* details ); -extern IRStmt* IRStmt_Exit ( IRExpr* guard, IRJumpKind jk, IRConst* dst ); +extern IRStmt* IRStmt_Put ( Int off, IRExpr* data ); +extern IRStmt* IRStmt_PutI ( IRArray* descr, IRExpr* ix, Int bias, + IRExpr* data ); +extern IRStmt* IRStmt_Tmp ( IRTemp tmp, IRExpr* data ); +extern IRStmt* IRStmt_STle ( IRExpr* addr, IRExpr* data ); +extern IRStmt* IRStmt_Dirty ( IRDirty* details ); +extern IRStmt* IRStmt_MFence ( void ); +extern IRStmt* IRStmt_Exit ( IRExpr* guard, IRJumpKind jk, IRConst* dst ); extern IRStmt* dopyIRStmt ( IRStmt* );