/* CONST */
static Addr32 guest_eip;
-/* The BBIR* into which we're generating code. */
-/* What it points to changes as we work through the bb. */
+/* The IRBB* into which we're generating code. */
static IRBB* irbb;
-/* Points to the last stmt added to the statement list of irbb, so
- that when a new stmt is added, the .link field can be correctly
- updated. */
-static IRStmt* last_stmt;
-
/* Add a statement to the list held by "irbb". */
-static void stmt ( IRStmt* stmt )
+static void stmt ( IRStmt* st )
{
- stmt->link = NULL;
- if (irbb->stmts == NULL) {
- irbb->stmts = stmt;
- } else {
- last_stmt->link = stmt;
- }
- last_stmt = stmt;
+ addStmtToIRBB( irbb, st );
}
/* Generate a new temporary of the given type. */
UChar sorb = 0;
/* For printing the stmts after each insn. */
- IRStmt* first_stmt = last_stmt;
+ Int first_stmt_idx = irbb->stmts_used;
*isEnd = False;
addr = t1 = t2 = INVALID_IRTEMP;
decode_success:
/* All decode successes end up here. */
DIP("\n");
- if (first_stmt == NULL)
- first_stmt = irbb->stmts;
- else
- first_stmt = first_stmt->link;
- while (True) {
- if (!first_stmt)
- break;
- if (print_codegen) {
- vex_printf(" ");
- ppIRStmt(first_stmt);
- vex_printf("\n");
- }
- if (first_stmt == last_stmt)
- break;
- first_stmt = first_stmt->link;
+ { Int i;
+ for (i = first_stmt_idx; i < irbb->stmts_used; i++) {
+ if (print_codegen) {
+ vex_printf(" ");
+ ppIRStmt(irbb->stmts[i]);
+ vex_printf("\n");
+ }
+ }
}
if (*isEnd) {
vassert(irbb->next != NULL);
print_codegen = (vex_verbosity >= 1);
guest_code = x86code;
guest_eip = (Addr32)eip;
- irbb = mkIRBB( newIRTypeEnv(), NULL, NULL, Ijk_Boring );
- last_stmt = NULL;
+ irbb = emptyIRBB();
vassert((eip >> 32) == 0);
vassert(vex_guest_insns_per_bb >= 1);
vassert(sz == 1 || sz == 2);
return i;
}
+X86Instr* X86Instr_Set32 ( X86CondCode cond, HReg dst ) {
+ X86Instr* i = LibVEX_Alloc(sizeof(X86Instr));
+ i->tag = Xin_Set32;
+ i->Xin.Set32.cond = cond;
+ i->Xin.Set32.dst = dst;
+ return i;
+}
X86Instr* X86Instr_FpUnary ( X86FpOp op, HReg src, HReg dst ) {
X86Instr* i = LibVEX_Alloc(sizeof(X86Instr));
i->tag = Xin_FpUnary;
vex_printf(",");
ppX86AMode(i->Xin.Store.dst);
return;
+ case Xin_Set32:
+ vex_printf("setl%s ", showX86CondCode(i->Xin.Set32.cond));
+ ppHRegX86(i->Xin.Set32.dst);
+ return;
case Xin_FpUnary:
vex_printf("g%sD ", showX86FpOp(i->Xin.FpUnary.op));
ppHRegX86(i->Xin.FpUnary.src);
addHRegUse(u, HRmRead, i->Xin.Store.src);
addRegUsage_X86AMode(u, i->Xin.Store.dst);
return;
+ case Xin_Set32:
+ addHRegUse(u, HRmWrite, i->Xin.Set32.dst);
+ return;
case Xin_FpUnary:
addHRegUse(u, HRmRead, i->Xin.FpUnary.src);
addHRegUse(u, HRmWrite, i->Xin.FpUnary.dst);
mapReg(m, &i->Xin.Store.src);
mapRegs_X86AMode(m, i->Xin.Store.dst);
return;
+ case Xin_Set32:
+ mapReg(m, &i->Xin.Set32.dst);
+ return;
case Xin_FpBinary:
mapReg(m, &i->Xin.FpBinary.srcL);
mapReg(m, &i->Xin.FpBinary.srcR);
*p++ = 0x68;
p = emit32(p, i->Xin.Push.src->Xrmi.Imm.imm32);
goto done;
+ case Xrmi_Reg:
+ *p++ = 0x50 + iregNo(i->Xin.Push.src->Xrmi.Reg.reg);
+ goto done;
default:
goto bad;
}
}
break;
+ case Xin_Set32:
+ /* Make the destination register be 1 or 0, depending on whether
+ the relevant condition holds. We have to dodge and weave
+ when the destination is %esi or %edi as we cannot directly
+ emit the native 'setb %reg' for those. Further complication:
+ the top 24 bits of the destination should be forced to zero,
+ but doing 'xor %r,%r' kills the flag(s) we are about to read.
+ Sigh. */
+ /* First: movl $0, %dst */
+ *p++ = 0xB8 + iregNo(i->Xin.Set32.dst);
+ p = emit32(p, 0);
+ /* Do we need to swap in %eax? */
+
+ if (iregNo(i->Xin.Set32.dst) >= 4) {
+ /* xchg %eax, %dst */
+ *p++ = 0x90 + iregNo(i->Xin.Set32.dst);
+ /* setb lo8(%reg) */
+ *p++ = 0x0F;
+ *p++ = 0x90 + (UChar)(i->Xin.Set32.cond);
+ p = doAMode_R(p, fake(0), hregX86_EAX());
+ /* xchg %eax, %dst */
+ *p++ = 0x90 + iregNo(i->Xin.Set32.dst);
+ } else {
+ /* setb lo8(%reg) */
+ *p++ = 0x0F;
+ *p++ = 0x90 + (UChar)(i->Xin.Set32.cond);
+ p = doAMode_R(p, fake(0), i->Xin.Set32.dst);
+ }
+ goto done;
+
case Xin_Store:
if (i->Xin.Store.sz == 2) {
/* This case, at least, is simple, given that we can
/* ffree %st(7) */
p = do_ffree_st7(p);
/* pushl %hi ; pushl %lo */
- *p++ = 0x50 + hregNumber(i->Xin.FpI64.iregHi);
- *p++ = 0x50 + hregNumber(i->Xin.FpI64.iregLo);
+ *p++ = 0x50 + iregNo(i->Xin.FpI64.iregHi);
+ *p++ = 0x50 + iregNo(i->Xin.FpI64.iregLo);
/* fildll 0(%esp) */
*p++ = 0xDF; *p++ = 0x6C; *p++ = 0x24; *p++ = 0x00;
/* addl $8, %esp */
*p++ = 0x83; *p++ = 0xC4; *p++ = 0x08;
- p = do_fstp_st(p, 1+hregNumber(i->Xin.FpI64.freg));
+ p = do_fstp_st(p, 1+iregNo(i->Xin.FpI64.freg));
goto done;
}
Xin_CMov32, /* conditional move */
Xin_LoadEX, /* mov{s,z}{b,w}l from mem to reg */
Xin_Store, /* store 16/8 bit value in memory */
+ Xin_Set32, /* convert condition code to 32-bit value */
Xin_FpUnary, /* FP fake unary op */
Xin_FpBinary, /* FP fake binary op */
Xin_FpLdSt, /* FP fake load/store */
HReg src;
X86AMode* dst;
} Store;
+ /* Convert a x86 condition code to a 32-bit value (0 or 1). */
+ struct {
+ X86CondCode cond;
+ HReg dst;
+ } Set32;
/* X86 Floating point (fake 3-operand, "flat reg file" insns) */
struct {
X86FpOp op;
extern X86Instr* X86Instr_LoadEX ( UChar szSmall, Bool syned,
X86AMode* src, HReg dst );
extern X86Instr* X86Instr_Store ( UChar sz, HReg src, X86AMode* dst );
+extern X86Instr* X86Instr_Set32 ( X86CondCode cond, HReg dst );
extern X86Instr* X86Instr_FpUnary ( X86FpOp op, HReg src, HReg dst );
extern X86Instr* X86Instr_FpBinary ( X86FpOp op, HReg srcL, HReg srcR, HReg dst );
extern X86Instr* X86Instr_FpLdSt ( Bool isLoad, UChar sz, HReg reg, X86AMode* );
static X86AMode* iselIntExpr_AMode ( ISelEnv* env, IRExpr* e );
static void iselIntExpr64 ( HReg* rHi, HReg* rLo,
ISelEnv* env, IRExpr* e );
+static X86CondCode iselCondCode ( ISelEnv* env, IRExpr* e );
static X86Instr* mk_MOVsd_RR ( HReg src, HReg dst )
addInstr(env, X86Instr_Sh32(Xsh_SHR, shift, X86RM_Reg(dst)));
return dst;
}
+ case Iop_1Uto8: {
+ HReg dst = newVRegI(env);
+ X86CondCode cond = iselCondCode(env, e->Iex.Unop.arg);
+ addInstr(env, X86Instr_Set32(cond,dst));
+ return dst;
+ }
case Iop_16to8:
case Iop_32to8:
case Iop_32to16:
return Xcc_Z;
}
+ /* var */
+ if (e->tag == Iex_Tmp) {
+ HReg r32 = lookupIRTemp(env, e->Iex.Tmp.tmp);
+ HReg dst = newVRegI(env);
+ addInstr(env, mk_MOVsd_RR(r32,dst));
+ addInstr(env, X86Instr_Alu32R(Xalu_AND,X86RMI_Imm(1),dst));
+ return Xcc_NZ;
+ }
+
ppIRExpr(e);
vpanic("iselCondCode");
}
addInstr(env, mk_MOVsd_RR(rLo,dstLo) );
return;
}
+ if (ty == Ity_Bit) {
+ X86CondCode cond = iselCondCode(env, stmt->Ist.Tmp.expr);
+ HReg dst = lookupIRTemp(env, tmp);
+ addInstr(env, X86Instr_Set32(cond, dst));
+ return;
+ }
if (ty == Ity_F64) {
HReg dst = lookupIRTemp(env, tmp);
HReg src = iselDblExpr(env, stmt->Ist.Tmp.expr);
{
Int i, j;
HReg hreg, hregHI;
- IRStmt* stmt;
/* Make up an initial environment to use. */
ISelEnv* env = LibVEX_Alloc(sizeof(ISelEnv));
env->vreg_ctr = j;
/* Ok, finally we can iterate over the statements. */
- for (stmt = bb->stmts; stmt; stmt=stmt->link)
- iselStmt(env,stmt);
+ for (i = 0; i < bb->stmts_used; i++)
+ iselStmt(env,bb->stmts[i]);
iselNext(env,bb->next,bb->jumpkind);
}
+
+
void ppIRBB ( IRBB* bb )
{
- IRStmt* s;
+ Int i;
vex_printf("IRBB {\n");
ppIRTypeEnv(bb->tyenv);
vex_printf("\n");
- for (s = bb->stmts; s; s = s->link) {
+ for (i = 0; i < bb->stmts_used; i++) {
vex_printf( " ");
- ppIRStmt(s);
+ ppIRStmt(bb->stmts[i]);
vex_printf( "\n");
}
vex_printf( " goto {");
IRStmt* IRStmt_Put ( Int off, IRExpr* value ) {
IRStmt* s = LibVEX_Alloc(sizeof(IRStmt));
s->tag = Ist_Put;
- s->link = NULL;
s->Ist.Put.offset = off;
s->Ist.Put.expr = value;
return s;
UShort minoff, UShort maxoff ) {
IRStmt* s = LibVEX_Alloc(sizeof(IRStmt));
s->tag = Ist_PutI;
- s->link = NULL;
s->Ist.PutI.offset = off;
s->Ist.PutI.expr = value;
s->Ist.PutI.minoff = minoff;
IRStmt* IRStmt_Tmp ( IRTemp tmp, IRExpr* expr ) {
IRStmt* s = LibVEX_Alloc(sizeof(IRStmt));
s->tag = Ist_Tmp;
- s->link = NULL;
s->Ist.Tmp.tmp = tmp;
s->Ist.Tmp.expr = expr;
return s;
IRStmt* IRStmt_STle ( IRExpr* addr, IRExpr* value ) {
IRStmt* s = LibVEX_Alloc(sizeof(IRStmt));
s->tag = Ist_STle;
- s->link = NULL;
s->Ist.STle.addr = addr;
s->Ist.STle.data = value;
return s;
IRStmt* IRStmt_Exit ( IRExpr* cond, IRConst* dst ) {
IRStmt* s = LibVEX_Alloc(sizeof(IRStmt));
s->tag = Ist_Exit;
- s->link = NULL;
s->Ist.Exit.cond = cond;
s->Ist.Exit.dst = dst;
return s;
}
-/* Constructors -- IRBB */
+/* Constructors -- IRBB (sort of) */
-IRBB* mkIRBB ( IRTypeEnv* env, IRStmt* stmts,
- IRExpr* next, IRJumpKind jumpkind ) {
- IRBB* bb = LibVEX_Alloc(sizeof(IRBB));
- bb->tyenv = env;
- bb->stmts = stmts;
- bb->next = next;
- bb->jumpkind = jumpkind;
+IRBB* emptyIRBB ( void )
+{
+ IRBB* bb = LibVEX_Alloc(sizeof(IRBB));
+ bb->tyenv = emptyIRTypeEnv();
+ bb->stmts_used = 0;
+ bb->stmts_size = 8;
+ bb->stmts = LibVEX_Alloc(bb->stmts_size * sizeof(IRStmt*));
+ bb->next = NULL;
+ bb->jumpkind = Ijk_Boring;
return bb;
}
+void addStmtToIRBB ( IRBB* bb, IRStmt* st )
+{
+ Int i;
+ if (bb->stmts_used == bb->stmts_size) {
+ IRStmt** stmts2 = LibVEX_Alloc(2 * bb->stmts_size * sizeof(IRStmt*));
+ for (i = 0; i < bb->stmts_size; i++)
+ stmts2[i] = bb->stmts[i];
+ bb->stmts = stmts2;
+ bb->stmts_size *= 2;
+ }
+ vassert(bb->stmts_used < bb->stmts_size);
+ bb->stmts[bb->stmts_used] = st;
+ bb->stmts_used++;
+}
+
/*---------------------------------------------------------------*/
/*--- Primop types ---*/
/*---------------------------------------------------------------*/
-/*--- Helper functions for the IR ---*/
+/*--- Helper functions for the IR -- IR Type Environments ---*/
/*---------------------------------------------------------------*/
-IRTypeEnv* newIRTypeEnv ( void )
+/* Make a new, empty IRTypeEnv. */
+
+IRTypeEnv* emptyIRTypeEnv ( void )
{
IRTypeEnv* env = LibVEX_Alloc(sizeof(IRTypeEnv));
env->types = LibVEX_Alloc(8 * sizeof(IRType));
return env;
}
+/* Make an exact copy of the given IRTypeEnv, usually so we can
+ add new stuff to the copy without messing up the original. */
+
+IRTypeEnv* copyIRTypeEnv ( IRTypeEnv* src )
+{
+ Int i;
+ IRTypeEnv* dst = LibVEX_Alloc(sizeof(IRTypeEnv));
+ dst->types_size = src->types_size;
+ dst->types_used = src->types_used;
+ dst->types = LibVEX_Alloc(dst->types_size * sizeof(IRType));
+ for (i = 0; i < src->types_used; i++)
+ dst->types[i] = src->types[i];
+ return dst;
+}
+
+/* Allocate a new IRTemp, given its type. */
IRTemp newIRTemp ( IRTypeEnv* env, IRType ty )
{
}
}
+/* Find the type of a temporary previously allocated in an
+ environment. */
IRType lookupIRTypeEnv ( IRTypeEnv* env, IRTemp tmp )
{
}
+/*---------------------------------------------------------------*/
+/*--- Helper functions for the IR -- finding types of exprs ---*/
+/*---------------------------------------------------------------*/
+
IRType typeOfIRConst ( IRConst* con )
{
switch (con->tag) {
vassert(guest_word_size == Ity_I32
|| guest_word_size == Ity_I64);
+ if (bb->stmts_used < 0 || bb->stmts_size < 8
+ || bb->stmts_used > bb->stmts_size)
+ /* this BB is so strange we can't even print it */
+ vpanic("sanityCheckIRBB: stmts array limits wierd");
+
/* Ensure each temp has a plausible type. */
for (i = 0; i < n_temps; i++) {
IRType ty = lookupIRTypeEnv(bb->tyenv,(IRTemp)i);
for (i = 0; i < n_temps; i++)
def_counts[i] = 0;
- for (stmt = bb->stmts; stmt; stmt=stmt->link) {
+ for (i = 0; i < bb->stmts_used; i++) {
+ stmt = bb->stmts[i];
useBeforeDef_Stmt(bb,stmt,def_counts);
if (stmt->tag == Ist_Tmp) {
if (stmt->Ist.Tmp.tmp < 0 || stmt->Ist.Tmp.tmp >= n_temps)
}
/* Typecheck everything. */
- for (stmt = bb->stmts; stmt; stmt=stmt->link)
- tcStmt( bb, stmt, guest_word_size );
+ for (i = 0; i < bb->stmts_used; i++)
+ tcStmt( bb, bb->stmts[i], guest_word_size );
if (typeOfIRExpr(bb->tyenv,bb->next) != guest_word_size)
sanityCheckFail(bb, NULL, "bb->next field has wrong type");
}
h->used++;
}
+/*---------------------------------------------------------------*/
+/*--- Flattening out a BB into pure SSA form ---*/
+/*---------------------------------------------------------------*/
+
+/* Clone the NULL-terminated vector of IRExpr*s attached to a
+ CCall. */
+
+static IRExpr** copyIexCCallArgs ( IRExpr** vec )
+{
+ Int i;
+ IRExpr** newvec;
+ for (i = 0; vec[i]; i++)
+ ;
+ newvec = LibVEX_Alloc((i+1)*sizeof(IRExpr*));
+ for (i = 0; vec[i]; i++)
+ newvec[i] = vec[i];
+ newvec[i] = NULL;
+ return newvec;
+}
+
+
+/* Flatten out 'ex' so it is atomic, returning a new expression with
+ the same value, after having appended extra IRTemp assignments to
+ the end of 'bb'. */
+
+static IRExpr* flatten_Expr ( IRBB* bb, IRExpr* ex )
+{
+ Int i;
+ IRExpr** newargs;
+ IRType ty = typeOfIRExpr(bb->tyenv, ex);
+ IRTemp t1;
+
+ switch (ex->tag) {
+
+ case Iex_Get:
+ t1 = newIRTemp(bb->tyenv, ty);
+ addStmtToIRBB(bb,
+ IRStmt_Tmp(t1, ex));
+ return IRExpr_Tmp(t1);
+
+ case Iex_Binop:
+ t1 = newIRTemp(bb->tyenv, ty);
+ addStmtToIRBB(bb, IRStmt_Tmp(t1,
+ IRExpr_Binop(ex->Iex.Binop.op,
+ flatten_Expr(bb, ex->Iex.Binop.arg1),
+ flatten_Expr(bb, ex->Iex.Binop.arg2))));
+ return IRExpr_Tmp(t1);
+
+ case Iex_Unop:
+ t1 = newIRTemp(bb->tyenv, ty);
+ addStmtToIRBB(bb, IRStmt_Tmp(t1,
+ IRExpr_Unop(ex->Iex.Unop.op,
+ flatten_Expr(bb, ex->Iex.Unop.arg))));
+ return IRExpr_Tmp(t1);
+
+ case Iex_LDle:
+ t1 = newIRTemp(bb->tyenv, ty);
+ addStmtToIRBB(bb, IRStmt_Tmp(t1,
+ IRExpr_LDle(ex->Iex.LDle.ty,
+ flatten_Expr(bb, ex->Iex.LDle.addr))));
+ return IRExpr_Tmp(t1);
+
+ case Iex_CCall:
+ newargs = copyIexCCallArgs(ex->Iex.CCall.args);
+ for (i = 0; newargs[i]; i++)
+ newargs[i] = flatten_Expr(bb, newargs[i]);
+ t1 = newIRTemp(bb->tyenv, ty);
+ addStmtToIRBB(bb, IRStmt_Tmp(t1,
+ IRExpr_CCall(ex->Iex.CCall.name,
+ ex->Iex.CCall.retty,
+ newargs)));
+ return IRExpr_Tmp(t1);
+
+ case Iex_Mux0X:
+ t1 = newIRTemp(bb->tyenv, ty);
+ addStmtToIRBB(bb, IRStmt_Tmp(t1,
+ IRExpr_Mux0X(flatten_Expr(bb, ex->Iex.Mux0X.cond),
+ flatten_Expr(bb, ex->Iex.Mux0X.expr0),
+ flatten_Expr(bb, ex->Iex.Mux0X.exprX))));
+ return IRExpr_Tmp(t1);
+
+ case Iex_Const:
+ case Iex_Tmp:
+ return ex;
+
+ default:
+ vex_printf("\n");
+ ppIRExpr(ex);
+ vex_printf("\n");
+ vpanic("flatten_Expr");
+ }
+}
+
+
+/* Append a completely flattened form of 'st' to the end of 'bb'. */
+
+static void flatten_Stmt ( IRBB* bb, IRStmt* st )
+{
+ IRExpr *e1, *e2;
+ switch (st->tag) {
+ case Ist_Put:
+ e1 = flatten_Expr(bb, st->Ist.Put.expr);
+ addStmtToIRBB(bb, IRStmt_Put(st->Ist.Put.offset, e1));
+ break;
+#if 0
+ case Ist_PutI:
+ e1 = flatten_Expr(bb, st->Ist.PutI.offset);
+ e2 = flatten_Expr(bb, st->Ist.PutI.expr);
+ addStmtToIRBB(bb, IRStmt_PutI(e1, e2, st->Ist.PutI.minoff,
+ st->Ist.PutI.maxoff));
+ break;
+#endif
+ case Ist_Tmp:
+ e1 = flatten_Expr(bb, st->Ist.Tmp.expr);
+ addStmtToIRBB(bb, IRStmt_Tmp(st->Ist.Tmp.tmp, e1));
+ break;
+ case Ist_STle:
+ e1 = flatten_Expr(bb, st->Ist.STle.addr);
+ e2 = flatten_Expr(bb, st->Ist.STle.data);
+ addStmtToIRBB(bb, IRStmt_STle(e1,e2));
+ break;
+ case Ist_Exit:
+ e1 = flatten_Expr(bb, st->Ist.Exit.cond);
+ addStmtToIRBB(bb, IRStmt_Exit(e1, st->Ist.Exit.dst));
+ break;
+ default:
+ vex_printf("\n");
+ ppIRStmt(st);
+ vex_printf("\n");
+ vpanic("flatten_Stmt");
+ }
+}
+
+static IRBB* flatten_BB ( IRBB* in )
+{
+ Int i;
+ IRBB* out;
+ out = emptyIRBB();
+ out->tyenv = copyIRTypeEnv( in->tyenv );
+ for (i = 0; i < in->stmts_used; i++)
+ flatten_Stmt( out, in->stmts[i] );
+ out->next = flatten_Expr( out, in->next );
+ out->jumpkind = in->jumpkind;
+ return out;
+}
+
/*---------------------------------------------------------------*/
/*--- iropt main ---*/
/* exported from this file */
IRBB* do_iropt_BB ( IRBB* bb0 )
{
- return bb0;
+ IRBB* flat = flatten_BB ( bb0 );
+ return flat;
}
/* Clean it up, hopefully a lot. */
irbb = do_iropt_BB ( irbb );
+ sanityCheckIRBB(irbb, Ity_I32);
if (vex_verbosity > 0) {
vex_printf("\n-------- After IR optimisation --------\n");
IRConst* dst;
} Exit;
} Ist;
- struct _IRStmt* link;
}
IRStmt;
extern void ppIRTypeEnv ( IRTypeEnv* );
-/* Basic blocks contain 4 fields:
+/* Basic blocks contain:
- A table giving a type for each temp
- - A list of statements
+ - An expandable array of statements
- An expression of type 32 or 64 bits, depending on the
guest's word size, indicating the next destination.
+ - An indication of any special actions (JumpKind) needed
+ for this final jump.
*/
typedef
struct _IRBB {
IRTypeEnv* tyenv;
- IRStmt* stmts;
+ IRStmt** stmts;
+ Int stmts_size;
+ Int stmts_used;
IRExpr* next;
IRJumpKind jumpkind;
}
IRBB;
-extern IRBB* mkIRBB ( IRTypeEnv*, IRStmt*, IRExpr*, IRJumpKind );
+extern IRBB* emptyIRBB ( void );
+extern void addStmtToIRBB ( IRBB*, IRStmt* );
extern void ppIRBB ( IRBB* );
/*---------------------------------------------------------------*/
/* For messing with IR type environments */
-extern IRTypeEnv* newIRTypeEnv ( void );
+extern IRTypeEnv* emptyIRTypeEnv ( void );
extern IRTemp newIRTemp ( IRTypeEnv*, IRType );
extern IRType lookupIRTypeEnv ( IRTypeEnv*, IRTemp );
+extern IRTypeEnv* copyIRTypeEnv ( IRTypeEnv* );
/* What is the type of this expression? */
extern IRType typeOfIRConst ( IRConst* );