+static ir_node *create_push(ia32_transform_env_t *env, ir_node *schedpoint, ir_node *sp, ir_node *mem, entity *ent) {
+ ir_node *noreg = ia32_new_NoReg_gp(env->cg);
+ ir_node *frame = get_irg_frame(env->irg);
+
+ ir_node *push = new_rd_ia32_Push(env->dbg, env->irg, env->block, frame, noreg, noreg, sp, mem);
+
+ set_ia32_frame_ent(push, ent);
+ set_ia32_use_frame(push);
+ set_ia32_op_type(push, ia32_AddrModeS);
+ set_ia32_am_flavour(push, ia32_B);
+ set_ia32_ls_mode(push, mode_Is);
+
+ sched_add_before(schedpoint, push);
+ return push;
+}
+
+static ir_node *create_pop(ia32_transform_env_t *env, ir_node *schedpoint, ir_node *sp, entity *ent) {
+ ir_node *noreg = ia32_new_NoReg_gp(env->cg);
+ ir_node *frame = get_irg_frame(env->irg);
+
+ ir_node *pop = new_rd_ia32_Pop(env->dbg, env->irg, env->block, frame, noreg, sp, new_NoMem());
+
+ set_ia32_frame_ent(pop, ent);
+ set_ia32_use_frame(pop);
+ set_ia32_op_type(pop, ia32_AddrModeD);
+ set_ia32_am_flavour(pop, ia32_B);
+ set_ia32_ls_mode(pop, mode_Is);
+
+ sched_add_before(schedpoint, pop);
+
+ return pop;
+}
+
+static ir_node* create_spproj(ia32_transform_env_t *env, ir_node *pred, int pos, ir_node *schedpoint) {
+ ir_mode *spmode = mode_Iu;
+ const arch_register_t *spreg = &ia32_gp_regs[REG_ESP];
+ ir_node *sp;
+
+ sp = new_rd_Proj(env->dbg, env->irg, env->block, pred, spmode, pos);
+ arch_set_irn_register(env->cg->arch_env, sp, spreg);
+ sched_add_before(schedpoint, sp);
+
+ return sp;
+}
+
+/**
+ * Transform memperm, currently we do this the ugly way and produce
+ * push/pop into/from memory cascades. This is possible without using
+ * any registers.
+ */
+static void transform_MemPerm(ia32_transform_env_t *env) {
+ ir_node *node = env->irn;
+ int i, arity;
+ ir_node *sp = be_abi_get_ignore_irn(env->cg->birg->abi, &ia32_gp_regs[REG_ESP]);
+ const ir_edge_t *edge;
+ const ir_edge_t *next;
+ ir_node **pops;
+
+ arity = be_get_MemPerm_entity_arity(node);
+ pops = alloca(arity * sizeof(pops[0]));
+
+ // create pushs
+ for(i = 0; i < arity; ++i) {
+ entity *ent = be_get_MemPerm_in_entity(node, i);
+ ir_type *enttype = get_entity_type(ent);
+ int entbits = get_type_size_bits(enttype);
+ ir_node *mem = get_irn_n(node, i + 1);
+ ir_node *push;
+
+ assert( (entbits == 32 || entbits == 64) && "spillslot on x86 should be 32 or 64 bit");
+
+ push = create_push(env, node, sp, mem, ent);
+ sp = create_spproj(env, push, 0, node);
+ if(entbits == 64) {
+ // add another push after the first one
+ push = create_push(env, node, sp, mem, ent);
+ add_ia32_am_offs_int(push, 4);
+ sp = create_spproj(env, push, 0, node);
+ }
+
+ set_irn_n(node, i, new_Bad());
+ }
+
+ // create pops
+ for(i = arity - 1; i >= 0; --i) {
+ entity *ent = be_get_MemPerm_out_entity(node, i);
+ ir_type *enttype = get_entity_type(ent);
+ int entbits = get_type_size_bits(enttype);
+
+ ir_node *pop;
+
+ assert( (entbits == 32 || entbits == 64) && "spillslot on x86 should be 32 or 64 bit");
+
+ pop = create_pop(env, node, sp, ent);
+ if(entbits == 64) {
+ // add another pop after the first one
+ sp = create_spproj(env, pop, 1, node);
+ pop = create_pop(env, node, sp, ent);
+ add_ia32_am_offs_int(pop, 4);
+ }
+ sp = create_spproj(env, pop, 1, node);
+
+ pops[i] = pop;
+ }
+
+ // exchange memprojs
+ foreach_out_edge_safe(node, edge, next) {
+ ir_node *proj = get_edge_src_irn(edge);
+ int p = get_Proj_proj(proj);
+
+ assert(p < arity);
+
+ set_Proj_pred(proj, pops[p]);
+ set_Proj_proj(proj, 3);
+ }
+
+ // remove memperm
+ arity = get_irn_arity(node);
+ for(i = 0; i < arity; ++i) {
+ set_irn_n(node, i, new_Bad());
+ }
+ sched_remove(node);
+}
+