diff --git a/arm-asm.c b/arm-asm.c index 99f2f3aa..af71dd9a 100644 --- a/arm-asm.c +++ b/arm-asm.c @@ -1247,10 +1247,87 @@ ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier) /* generate prolog and epilog code for asm statement */ ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, - int nb_outputs, int is_output, - uint8_t *clobber_regs, - int out_reg) + int nb_outputs, int is_output, + uint8_t *clobber_regs, + int out_reg) { + uint8_t regs_allocated[NB_ASM_REGS]; + ASMOperand *op; + int i, reg; + uint32_t saved_regset = 0; + + // TODO: Check non-E ABI. + // Note: Technically, r13 (sp) is also callee-saved--but that does not matter yet + static uint8_t reg_saved[] = { 4, 5, 6, 7, 8, 9 /* Note: sometimes special reg "sb" */ , 10, 11 }; + + /* mark all used registers */ + memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated)); + for(i = 0; i < nb_operands;i++) { + op = &operands[i]; + if (op->reg >= 0) + regs_allocated[op->reg] = 1; + } + for(i = 0; i < sizeof(reg_saved)/sizeof(reg_saved[0]); i++) { + reg = reg_saved[i]; + if (regs_allocated[reg]) + saved_regset |= 1 << reg; + } + + if (!is_output) { // prolog + /* generate reg save code */ + if (saved_regset) + gen_le32(0xe92d0000 | saved_regset); // push {...} + + /* generate load code */ + for(i = 0; i < nb_operands; i++) { + op = &operands[i]; + if (op->reg >= 0) { + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && + op->is_memory) { + /* memory reference case (for both input and + output cases) */ + SValue sv; + sv = *op->vt; + sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL | VT_LVAL; + sv.type.t = VT_PTR; + load(op->reg, &sv); + } else if (i >= nb_outputs || op->is_rw) { // not write-only + /* load value in register */ + load(op->reg, op->vt); + if (op->is_llong) + tcc_error("long long not implemented"); + } + } + } + } else { // epilog + /* generate save code */ + for(i = 0 ; i < nb_outputs; i++) { + op = &operands[i]; + if (op->reg >= 0) { + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { + if (!op->is_memory) { + SValue sv; + sv = *op->vt; + sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; + sv.type.t = VT_PTR; + load(out_reg, &sv); + + sv = *op->vt; + sv.r = (sv.r & ~VT_VALMASK) | out_reg; + store(op->reg, &sv); + } + } else { + store(op->reg, op->vt); + if (op->is_llong) + tcc_error("long long not implemented"); + } + } + } + + /* generate reg restore code */ + if (saved_regset) + gen_le32(0xe8bd0000 | saved_regset); // pop {...} + } } ST_FUNC void asm_compute_constraints(ASMOperand *operands,