From 152815e9ecfcb6ed0c4eacf25621123a99a20cb0 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 24 Feb 2025 17:31:08 -0800 Subject: [PATCH] add prolog_genva_start() (#20909) --- compiler/src/dmd/backend/arm/cod3.d | 163 +++++++++++++++++++++++--- compiler/src/dmd/backend/backconfig.d | 2 +- compiler/src/dmd/backend/cgelem.d | 6 +- compiler/src/dmd/backend/x86/cod3.d | 3 + 4 files changed, 157 insertions(+), 17 deletions(-) diff --git a/compiler/src/dmd/backend/arm/cod3.d b/compiler/src/dmd/backend/arm/cod3.d index 7f0ebee85e0..4d386309b0d 100644 --- a/compiler/src/dmd/backend/arm/cod3.d +++ b/compiler/src/dmd/backend/arm/cod3.d @@ -329,26 +329,159 @@ void prolog_genvarargs(ref CGstate cg, ref CodeBuilder cdb, Symbol* sv) cdb.gen1(INSTR.str_imm_fpsimd(0,2,offset,29,q)); } } +} + +/******************************** + * Generate elems for va_start() + * Params: + * sv = symbol for __va_argsave + * parmn = last named parameter + */ +@trusted +elem* prolog_genva_start(Symbol* sv, Symbol* parmn) +{ + printf("prolog_genva_start()\n"); + + /* the stack variable __va_argsave points to an instance of: + * struct __va_argsave_t { + * size_t[Vregnum] regs; + * real[8] fpregs; + * struct __va_list_tag { + * uint offset_regs; + * uint offset_fpregs; + * void* stack_args; + * void* reg_args; + * } + * // AArch64 Procedure Call Standard 12.2 + * struct __va_list_tag { + * void* stack; // next stack param + * void* gr_top; // end of GP arg reg save area + * void* vr_top; // end of FP/SIMD arg reg save area + * int gr_offs; // offset from gr_top to next GP register arg + * int vr_offs; // offset from vr_top to next FP/SIMD register arg + * } + * void* stack_args_save; + * } + */ + + enum OFF // offsets into __va_argsave_t + { + Offset_stack, + Offset_gr_top, + Offset_vr_top, + Offset_gr_offs, + Offset_vr_offs, + + Offset_regs = 8*8 + 8*16, + Offset_fpregs = Offset_regs + 4, + Stack_args = Offset_fpregs + 4, + Reg_args = Stack_args + 8, + Stack_args_save = Reg_args + 8, + } + + regm_t namedargs = prolog_namedArgs(); + + // AArch64 Procedure Call Standard 12.3 + uint named_gr; // number of general registers known to hold named incoming arguments + foreach (reg_t x; 0 .. 8) + { + if (!namedargs) + break; + regm_t m = mask(x); + if (m & namedargs) + { + ++named_gr; + namedargs &= ~m; + } + } + + uint named_vr; // number of FP/SIMD registers known to hold named incoming arguments + foreach (reg_t q; 32 + 0 .. 32 + 8) + { + if (!namedargs) + break; + regm_t m = mask(q); + if (m & namedargs) + { + ++named_vr; + namedargs &= ~m; + } + } + + // set stack to address following the last named incoming argument on the stack + // rounded upwards to a multiple of 8 bytes, or if there are no named arguments on the stack, then + // the value of the stack pointer when the function was entered. + elem* e1 = null; // TODO + + // set gr_top to address following the general register save area + elem* e2 = el_bin(OPeq, TYptr, el_var(sv), el_ptr(sv)); + e2.E1.Voffset = OFF.Offset_vr_offs; -static if (0) // TODO + // set vr_top to address following the FP/SIMD register save area + elem* ex3 = el_bin(OPadd, TYptr, el_ptr(sv), el_long(TYlong, 8 * 8)); + elem* e3 = el_bin(OPeq,TYptr,el_var(sv),ex3); + e3.E1.Ety = TYptr; + e3.E1.Voffset = OFF.Offset_vr_offs; + + // set gr_offs + elem* e4 = el_bin(OPeq, TYint, el_var(sv), el_long(TYint, 0 - ((8 - named_gr) * 8))); + e4.E1.Ety = TYint; + e4.E1.Voffset = OFF.Offset_gr_offs; + + // set vr_offs + elem* e5 = el_bin(OPeq, TYint, el_var(sv), el_long(TYint, 0 - ((8 - named_vr) * 16))); + e5.E1.Ety = TYint; + e5.E1.Voffset = OFF.Offset_vr_offs; + +static if (0) { - // LEA R11, Para.size+Para.offset[RBP] - uint ea2 = modregxrm(2,R11,BPRM); - if (!cg.hasframe) - ea2 = (modregrm(0,4,SP) << 8) | modregrm(2,DX,4); - cg.Para.offset = (cg.Para.offset + (REGSIZE - 1)) & ~(REGSIZE - 1); - cdb.genc1(LEA,(REX_W << 16) | ea2,FL.const_,cg.Para.size + cg.Para.offset); - - // MOV 9+16[RAX],R11 - cdb.genc1(0x89,(REX_W << 16) | modregxrm(2,R11,AX),FL.const_,9 + 16); // into stack_args_save - - pinholeopt(cdb.peek(), null); - useregs(mAX|mR11); + // set offset_regs + elem* e1 = el_bin(OPeq, TYint, el_var(sv), el_long(TYint, offset_regs)); + e1.E1.Ety = TYint; + e1.E1.Voffset = OFF.Offset_regs; + + // set offset_fpregs + elem* e2 = el_bin(OPeq, TYint, el_var(sv), el_long(TYint, offset_fpregs)); + e2.E1.Ety = TYint; + e2.E1.Voffset = OFF.Offset_fpregs; + + // set reg_args + elem* e4 = el_bin(OPeq, TYnptr, el_var(sv), el_ptr(sv)); + e4.E1.Ety = TYnptr; + e4.E1.Voffset = OFF.Reg_args; + + // set stack_args + /* which is a pointer to the first variadic argument on the stack. + * Normally, we could set it by taking the address of the last named parameter + * (parmn) and then skipping past it. The trouble, though, is it fails + * when all the named parameters get passed in a register. + * elem* e3 = el_bin(OPeq, TYnptr, el_var(sv), el_ptr(parmn)); + * e3.E1.Ety = TYnptr; + * e3.E1.Voffset = OFF.Stack_args; + * auto sz = type_size(parmn.Stype); + * sz = (sz + (REGSIZE - 1)) & ~(REGSIZE - 1); + * e3.E2.Voffset += sz; + * The next possibility is to do it the way prolog_genvarargs() does: + * LEA R11, Para.size+Para.offset[RBP] + * The trouble there is Para.size and Para.offset is not available when + * this function is called. It might be possible to compute this earlier.(1) + * Another possibility is creating a special operand type that gets filled + * in after the prolog_genvarargs() is called. + * Or do it this simpler way - compute the needed value in prolog_genvarargs(), + * and save it in a slot just after va_argsave, called `stack_args_save`. + * Then, just copy from `stack_args_save` to `stack_args`. + * Although, doing (1) might be optimal. + */ + elem* e3 = el_bin(OPeq, TYnptr, el_var(sv), el_var(sv)); + e3.E1.Ety = TYnptr; + e3.E1.Voffset = OFF.Stack_args; + e3.E2.Ety = TYnptr; + e3.E2.Voffset = OFF.Stack_args_save; } + elem* e = el_combine(e1, el_combine(e2, el_combine(e3, el_combine(e4, e5)))); + return e; } -// prolog_genvarargs -// prolog_genva_start // prolog_gen_win64_varargs // prolog_namedArgs // prolog_loadparams diff --git a/compiler/src/dmd/backend/backconfig.d b/compiler/src/dmd/backend/backconfig.d index 919c3eb421c..ab9d9d33ed3 100644 --- a/compiler/src/dmd/backend/backconfig.d +++ b/compiler/src/dmd/backend/backconfig.d @@ -101,7 +101,7 @@ void out_config_init( cfg.fulltypes = CVNONE; cfg.fpxmmregs = false; if (!arm) - cfg.inline8087 = 1; + cfg.inline8087 = 1; cfg.memmodel = 0; cfg.flags |= CFGuchar; // make sure TYchar is unsigned cfg.exe = exefmt; diff --git a/compiler/src/dmd/backend/cgelem.d b/compiler/src/dmd/backend/cgelem.d index 00a34d0aed9..83e9795035c 100644 --- a/compiler/src/dmd/backend/cgelem.d +++ b/compiler/src/dmd/backend/cgelem.d @@ -5513,6 +5513,7 @@ private elem* elclassinit(elem* e, Goal goal) } /******************************************** + * Handle OPva_start */ @trusted @@ -5616,7 +5617,10 @@ if (config.exe & EX_posix) if (va_argsave) { e.E2 = el_ptr(va_argsave); - e.E2.Voffset = 6 * 8 + 8 * 16; // offset to struct __va_list_tag defined in sysv_x64.d + if (cgstate.AArch64) + e.E2.Voffset = 8 * 8 + 8 * 16; // offset to struct __va_list_tag + else + e.E2.Voffset = 6 * 8 + 8 * 16; // offset to struct __va_list_tag defined in sysv_x64.d return el_combine(prolog_genva_start(va_argsave, parmn.Vsym), e); } else diff --git a/compiler/src/dmd/backend/x86/cod3.d b/compiler/src/dmd/backend/x86/cod3.d index af2852917f7..0a5836307d0 100644 --- a/compiler/src/dmd/backend/x86/cod3.d +++ b/compiler/src/dmd/backend/x86/cod3.d @@ -4078,6 +4078,9 @@ void prolog_genvarargs(ref CGstate cg, ref CodeBuilder cdb, Symbol* sv) @trusted elem* prolog_genva_start(Symbol* sv, Symbol* parmn) { + if (cgstate.AArch64) + return dmd.backend.arm.cod3.prolog_genva_start(sv, parmn); + enum Vregnum = 6; /* the stack variable __va_argsave points to an instance of: