From e4d217211758844075bd39e26c40d3c8be6e2432 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Sat, 30 Jul 2016 15:54:22 -0700 Subject: [PATCH] all: handle FP offsets Remove both FP and SB as concrete registers; they are purely psuedo-registers. FP offsets are handled by adding the current SP adjustment to the offset of NAME_PARAM Addrs. I am unsure of my arg and frame sizes in riscv_hello.s. It is not clear to me if callers are supposed to leave room for return values in their frame size or if callees are supposed to add them to their argument size. The assembly in the runtime seems to be inconsistent. Fixes #8 Change-Id: Ib79efb4fea8629f22461b4ebfc987bc8785a3ce3 --- src/cmd/asm/internal/arch/arch.go | 5 ++ .../asm/internal/asm/testdata/riscv_hello.s | 9 ++-- src/cmd/internal/obj/riscv/asm.go | 51 +++++++++++-------- src/cmd/internal/obj/riscv/cpu.go | 2 - src/cmd/internal/obj/riscv/list.go | 9 ++-- 5 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/cmd/asm/internal/arch/arch.go b/src/cmd/asm/internal/arch/arch.go index d989e7511fa80..102dc592b1fd4 100644 --- a/src/cmd/asm/internal/arch/arch.go +++ b/src/cmd/asm/internal/arch/arch.go @@ -442,6 +442,11 @@ var riscvJumps = map[string]bool{ } func archRiscv() *Arch { + // Pseudo-registers. + riscv.Registers["SB"] = RSB + riscv.Registers["FP"] = RFP + riscv.Registers["PC"] = RPC + return &Arch{ LinkArch: &riscv.LinkRISCV, Instructions: riscv.Instructions, diff --git a/src/cmd/asm/internal/asm/testdata/riscv_hello.s b/src/cmd/asm/internal/asm/testdata/riscv_hello.s index 60bca2ae9710a..6f8e873df94db 100644 --- a/src/cmd/asm/internal/asm/testdata/riscv_hello.s +++ b/src/cmd/asm/internal/asm/testdata/riscv_hello.s @@ -2,18 +2,15 @@ #define SYS_WRITE 64 // func helloChar() uint64 -TEXT helloChar(SB),0,$8 +TEXT helloChar(SB),0,$0 MOV $72, T0 - // FIXME(mpratt): this should use FP. - MOV T0, 0(SP) + MOV T0, ret+0(FP) RET // _rt0_riscv_linux is the entry point. -TEXT mainĀ·main(SB),0,$8 +TEXT mainĀ·main(SB),0,$8-0 // Write "H" to stdout...not quite to hello world, yet CALL helloChar(SB) - MOV -8(SP), T0 - MOVW T0, 0(SP) MOV SP, A1 // ptr to data MOV $1, A0 // fd 1 for stdout MOV $1, A2 // len("H") == 1 diff --git a/src/cmd/internal/obj/riscv/asm.go b/src/cmd/internal/obj/riscv/asm.go index f47db9460705b..4cf1924b27462 100644 --- a/src/cmd/internal/obj/riscv/asm.go +++ b/src/cmd/internal/obj/riscv/asm.go @@ -41,13 +41,11 @@ import ( "fmt" ) -// resolvepseudoreg concretizes pseudo-registers in an Addr. -func resolvepseudoreg(a *obj.Addr) { - if a.Type == obj.TYPE_MEM { - switch a.Name { - case obj.NAME_PARAM: - a.Reg = REG_FP - } +// stackOffset updates Addr offsets based on the current stack size. +func stackOffset(a *obj.Addr, stacksize int64) { + switch a.Name { + case obj.NAME_AUTO, obj.NAME_PARAM: + a.Offset += stacksize } } @@ -108,11 +106,9 @@ func movtos(mnemonic obj.As) obj.As { } } -// addrtoreg extracts the register from an addr, handling SB and SP. +// addrtoreg extracts the register from an Addr, handling special Addr.Names. func addrtoreg(a obj.Addr) int16 { switch a.Name { - case obj.NAME_EXTERN: - return REG_SB case obj.NAME_PARAM, obj.NAME_AUTO: return REG_SP } @@ -164,11 +160,6 @@ func progedit(ctxt *obj.Link, p *obj.Prog) { } } - // Concretize pseudo-registers. - resolvepseudoreg(&p.From) - resolvepseudoreg(p.From3) - resolvepseudoreg(&p.To) - // Do additional single-instruction rewriting. switch p.As { // Turn JMP into JAL ZERO or JALR ZERO. @@ -272,10 +263,10 @@ func InvertBranch(i obj.As) obj.As { } } -// preprocess generates prologue and epilogue code and computes PC-relative -// branch and jump offsets. +// preprocess generates prologue and epilogue code, computes PC-relative branch +// and jump offsets, and resolves psuedo-registers. // -// preprocess is called once for each linker symbol. +// preprocess is called once per linker symbol. // // When preprocess finishes, all instructions in the symbol are either // concrete, real RISC-V instructions or directive pseudo-ops like TEXT, @@ -287,7 +278,12 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) { ctxt.Diag("preprocess: found symbol that does not start with TEXT directive") return } + stacksize := text.To.Offset + + cursym.Args = text.To.Val.(int32) + cursym.Locals = int32(stacksize) + // Insert stack adjustment if necessary. if stacksize != 0 { spadj := obj.Appendp(ctxt, text) @@ -300,11 +296,25 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) { spadj.Spadj = int32(-stacksize) } + // Update stack-based offsets. + for p := cursym.Text; p != nil; p = p.Link { + stackOffset(&p.From, stacksize) + if p.From3 != nil { + stackOffset(p.From3, stacksize) + } + stackOffset(&p.To, stacksize) + + // TODO: update stacksize when instructions that modify SP are + // found, or disallow it entirely. + } + // Additional instruction rewriting. for p := cursym.Text; p != nil; p = p.Link { switch p.As { - // Rewrite MOV. + // Rewrite MOV. This couldn't be done in progedit, as SP + // offsets needed to be applied before we split up some of the + // Addrs. case AMOV, AMOVB, AMOVH, AMOVW, AMOVBU, AMOVHU, AMOVWU: switch p.From.Type { case obj.TYPE_MEM: // MOV c(Rs), Rd -> L $c, Rs, Rd @@ -404,7 +414,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) { p.From.Type = obj.TYPE_CONST switch p.From.Name { case obj.NAME_EXTERN: - p.From3.Reg = REG_SB + // Doesn't matter, we'll add a + // relocation later. case obj.NAME_PARAM, obj.NAME_AUTO: p.From3.Reg = REG_SP default: diff --git a/src/cmd/internal/obj/riscv/cpu.go b/src/cmd/internal/obj/riscv/cpu.go index 6028adee5f0e1..e4bc5b5d4a28c 100644 --- a/src/cmd/internal/obj/riscv/cpu.go +++ b/src/cmd/internal/obj/riscv/cpu.go @@ -116,7 +116,6 @@ const ( REG_T1 = REG_X6 REG_T2 = REG_X7 REG_S0 = REG_X8 - REG_FP = REG_X8 // S0 and FP are the same. REG_S1 = REG_X9 REG_A0 = REG_X10 REG_A1 = REG_X11 @@ -142,7 +141,6 @@ const ( REG_T6 = REG_X31 // Go runtime register names. - REG_SB = REG_X3 // Static base. REG_G = REG_X4 // G pointer. REG_RT1 = REG_S2 // Reserved for runtime (duffzero and duffcopy). REG_RT2 = REG_S3 // Reserved for runtime (duffcopy). diff --git a/src/cmd/internal/obj/riscv/list.go b/src/cmd/internal/obj/riscv/list.go index 4702008eaad58..1765b45a15670 100644 --- a/src/cmd/internal/obj/riscv/list.go +++ b/src/cmd/internal/obj/riscv/list.go @@ -48,13 +48,12 @@ var ( REG_ZERO: "ZERO", REG_RA: "RA", REG_SP: "SP", - // REG_GP is REG_SB + REG_GP: "GP", // REG_TP is REG_G REG_T0: "T0", REG_T1: "T1", REG_T2: "T2", REG_S0: "S0", - // REG_FP is REG_S0. REG_S1: "S1", REG_A0: "A0", REG_A1: "A1", @@ -80,7 +79,6 @@ var ( REG_T6: "T6", // Go runtime register names. - REG_SB: "SB", REG_RT1: "RT1", REG_RT2: "RT2", REG_CTXT: "CTXT", @@ -122,6 +120,8 @@ var ( } ) +// initRegisters initializes the Registers map. arch.archRiscv will also add +// some psuedoregisters. func initRegisters() { // Standard register names. for i := REG_X0; i <= REG_X31; i++ { @@ -134,7 +134,6 @@ func initRegisters() { } // General registers with ABI names. - Registers["ZERO"] = REG_ZERO Registers["RA"] = REG_RA Registers["SP"] = REG_SP @@ -144,7 +143,6 @@ func initRegisters() { Registers["T1"] = REG_T1 Registers["T2"] = REG_T2 Registers["S0"] = REG_S0 - Registers["FP"] = REG_FP Registers["S1"] = REG_S1 Registers["A0"] = REG_A0 Registers["A1"] = REG_A1 @@ -170,7 +168,6 @@ func initRegisters() { Registers["T6"] = REG_T6 // Golang runtime register names. - Registers["SB"] = REG_SB Registers["RT1"] = REG_RT1 Registers["RT2"] = REG_RT2 Registers["CTXT"] = REG_CTXT