From 2ec5653f5ed27ff92483a0a435c5a050c19e4422 Mon Sep 17 00:00:00 2001 From: Haolan Date: Mon, 15 Sep 2025 13:38:33 +0800 Subject: [PATCH] feat: implement llgo.stackSave --- _demo/c/stacksave/stacksave_amd64.go | 26 +++++++++++++++++++++ _demo/c/stacksave/stacksave_arm64.go | 22 ++++++++++++++++++ cl/_testrt/stacksave/in.go | 14 ++++++++++++ cl/_testrt/stacksave/out.ll | 34 ++++++++++++++++++++++++++++ cl/import.go | 3 ++- cl/instr.go | 5 +++- ssa/eh.go | 16 +++++++++++++ ssa/package.go | 1 + 8 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 _demo/c/stacksave/stacksave_amd64.go create mode 100644 _demo/c/stacksave/stacksave_arm64.go create mode 100644 cl/_testrt/stacksave/in.go create mode 100644 cl/_testrt/stacksave/out.ll diff --git a/_demo/c/stacksave/stacksave_amd64.go b/_demo/c/stacksave/stacksave_amd64.go new file mode 100644 index 00000000..a54af21b --- /dev/null +++ b/_demo/c/stacksave/stacksave_amd64.go @@ -0,0 +1,26 @@ +//go:build amd64 + +package main + +import ( + "unsafe" + _ "unsafe" +) + +//go:linkname getsp llgo.stackSave +func getsp() unsafe.Pointer + +//go:linkname asmFull llgo.asm +func asmFull(instruction string, regs map[string]any) uintptr { return 0 } + +func main() { + var spPtr uintptr + + asmFull("movq sp, {{addr}}", map[string]any{ + "addr": unsafe.Pointer(&spPtr), + }) + + if spPtr != uintptr(getsp()) { + panic("invalid stack pointer") + } +} diff --git a/_demo/c/stacksave/stacksave_arm64.go b/_demo/c/stacksave/stacksave_arm64.go new file mode 100644 index 00000000..66815413 --- /dev/null +++ b/_demo/c/stacksave/stacksave_arm64.go @@ -0,0 +1,22 @@ +//go:build arm64 + +package main + +import ( + "unsafe" + _ "unsafe" +) + +//go:linkname getsp llgo.stackSave +func getsp() unsafe.Pointer + +//go:linkname asmFull llgo.asm +func asmFull(instruction string, regs map[string]any) uintptr { return 0 } + +func main() { + sp := asmFull("mov {}, sp", nil) + + if sp != uintptr(getsp()) { + panic("invalid stack pointer") + } +} diff --git a/cl/_testrt/stacksave/in.go b/cl/_testrt/stacksave/in.go new file mode 100644 index 00000000..409aafac --- /dev/null +++ b/cl/_testrt/stacksave/in.go @@ -0,0 +1,14 @@ +package main + +import ( + "unsafe" + _ "unsafe" +) + +//go:linkname getsp llgo.stackSave +func getsp() unsafe.Pointer + +func main() { + sp := getsp() + println(sp) +} diff --git a/cl/_testrt/stacksave/out.ll b/cl/_testrt/stacksave/out.ll new file mode 100644 index 00000000..1622ee90 --- /dev/null +++ b/cl/_testrt/stacksave/out.ll @@ -0,0 +1,34 @@ +; ModuleID = 'github.com/goplus/llgo/cl/_testrt/stacksave' +source_filename = "github.com/goplus/llgo/cl/_testrt/stacksave" + +@"github.com/goplus/llgo/cl/_testrt/stacksave.init$guard" = global i1 false, align 1 + +define void @"github.com/goplus/llgo/cl/_testrt/stacksave.init"() { +_llgo_0: + %0 = load i1, ptr @"github.com/goplus/llgo/cl/_testrt/stacksave.init$guard", align 1 + br i1 %0, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"github.com/goplus/llgo/cl/_testrt/stacksave.init$guard", align 1 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define void @"github.com/goplus/llgo/cl/_testrt/stacksave.main"() { +_llgo_0: + %0 = call ptr @llvm.stacksave() + call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintPointer"(ptr %0) + call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintByte"(i8 10) + ret void +} + +; Function Attrs: nocallback nofree nosync nounwind willreturn +declare ptr @llvm.stacksave() #0 + +declare void @"github.com/goplus/llgo/runtime/internal/runtime.PrintPointer"(ptr) + +declare void @"github.com/goplus/llgo/runtime/internal/runtime.PrintByte"(i8) + +attributes #0 = { nocallback nofree nosync nounwind willreturn } diff --git a/cl/import.go b/cl/import.go index ea472819..88e21394 100644 --- a/cl/import.go +++ b/cl/import.go @@ -503,7 +503,8 @@ const ( llgoCgoCheckPointer = llgoCgoBase + 0x6 llgoCgoCgocall = llgoCgoBase + 0x7 - llgoAsm = llgoInstrBase + 0x40 + llgoAsm = llgoInstrBase + 0x40 + llgoStackSave = llgoInstrBase + 0x41 llgoAtomicOpLast = llgoAtomicOpBase + int(llssa.OpUMin) ) diff --git a/cl/instr.go b/cl/instr.go index e007d826..2c7ad5e9 100644 --- a/cl/instr.go +++ b/cl/instr.go @@ -425,7 +425,8 @@ var llgoInstrs = map[string]int{ "_cgoCheckPointer": llgoCgoCheckPointer, "_cgo_runtime_cgocall": llgoCgoCgocall, - "asm": llgoAsm, + "asm": llgoAsm, + "stackSave": llgoStackSave, } // funcOf returns a function by name and set ftype = goFunc, cFunc, etc. @@ -601,6 +602,8 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon ret = p.sigsetjmp(b, args) case llgoSiglongjmp: p.siglongjmp(b, args) + case llgoStackSave: + ret = b.StackSave() case llgoSigjmpbuf: // func sigjmpbuf() ret = b.AllocaSigjmpBuf() case llgoDeferData: // func deferData() *Defer diff --git a/ssa/eh.go b/ssa/eh.go index d86f25f7..3b01cc80 100644 --- a/ssa/eh.go +++ b/ssa/eh.go @@ -91,6 +91,16 @@ func (p Program) tySiglongjmp() *types.Signature { return p.sigljmpTy } +// func() uintptr +func (p Program) tyStacksave() *types.Signature { + if p.stackSaveTy == nil { + paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type) + params := types.NewTuple(paramPtr) + p.stackSaveTy = types.NewSignatureType(nil, nil, nil, nil, params, false) + } + return p.stackSaveTy +} + func (b Builder) AllocaSigjmpBuf() Expr { prog := b.Prog n := unsafe.Sizeof(sigjmpbuf{}) @@ -98,6 +108,12 @@ func (b Builder) AllocaSigjmpBuf() Expr { return b.Alloca(size) } +// declare ptr @llvm.stacksave.p0() +func (b Builder) StackSave() Expr { + fn := b.Pkg.cFunc("llvm.stacksave", b.Prog.tyStacksave()) + return b.InlineCall(fn) +} + func (b Builder) Sigsetjmp(jb, savemask Expr) Expr { if b.Prog.target.GOARCH == "wasm" { return b.Setjmp(jb) diff --git a/ssa/package.go b/ssa/package.go index e90426d5..7a3daccf 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -195,6 +195,7 @@ type aProgram struct { mallocTy *types.Signature freeTy *types.Signature memsetInlineTy *types.Signature + stackSaveTy *types.Signature createKeyTy *types.Signature getSpecTy *types.Signature