diff --git a/_demo/asmcall/asmcall.go b/_demo/asmcall/asmcall.go new file mode 100644 index 00000000..324677a6 --- /dev/null +++ b/_demo/asmcall/asmcall.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + "time" +) + +//llgo:link asm llgo.asm +func asm(instruction string) {} + +func main() { + start := time.Now() + for i := 0; i < 100000; i++ { + asm("nop") + } + duration := time.Since(start) + fmt.Println("Duration:", duration) +} diff --git a/cl/_testrt/asm/in.go b/cl/_testrt/asm/in.go new file mode 100644 index 00000000..74d9a68c --- /dev/null +++ b/cl/_testrt/asm/in.go @@ -0,0 +1,10 @@ +package main + +import _ "unsafe" + +//go:linkname asm llgo.asm +func asm(instruction string) + +func main() { + asm("nop") +} diff --git a/cl/_testrt/asm/out.ll b/cl/_testrt/asm/out.ll new file mode 100644 index 00000000..164617e3 --- /dev/null +++ b/cl/_testrt/asm/out.ll @@ -0,0 +1,23 @@ +; ModuleID = 'github.com/goplus/llgo/cl/_testrt/asm' +source_filename = "github.com/goplus/llgo/cl/_testrt/asm" + +@"github.com/goplus/llgo/cl/_testrt/asm.init$guard" = global i1 false, align 1 + +define void @"github.com/goplus/llgo/cl/_testrt/asm.init"() { +_llgo_0: + %0 = load i1, ptr @"github.com/goplus/llgo/cl/_testrt/asm.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/asm.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/asm.main"() { +_llgo_0: + call void asm sideeffect "nop", ""() + ret void +} diff --git a/cl/import.go b/cl/import.go index b70d0046..91561e6b 100644 --- a/cl/import.go +++ b/cl/import.go @@ -506,6 +506,9 @@ const ( llgoCgoCheckPointer = llgoCgoBase + 0x6 llgoCgoCgocall = llgoCgoBase + 0x7 + llgoAsmBase = llgoInstrBase + 0x40 + llgoAsm = llgoAsmBase + 0x0 + llgoAtomicOpLast = llgoAtomicOpBase + int(llssa.OpUMin) ) diff --git a/cl/instr.go b/cl/instr.go index 2865fb2c..e988bc01 100644 --- a/cl/instr.go +++ b/cl/instr.go @@ -66,6 +66,17 @@ func cstr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) { panic("cstr(): invalid arguments") } +// func asm(string) +func asm(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) { + if len(args) == 1 { + if sv, ok := constStr(args[0]); ok { + b.InlineAsm(sv) + return llssa.Expr{Type: b.Prog.Void()} + } + } + panic("asm(): invalid arguments") +} + // ----------------------------------------------------------------------------- // func _Cfunc_CString(s string) *int8 @@ -329,6 +340,8 @@ var llgoInstrs = map[string]int{ "_Cfunc__CMalloc": llgoCgoCMalloc, "_cgoCheckPointer": llgoCgoCheckPointer, "_cgo_runtime_cgocall": llgoCgoCgocall, + + "asm": llgoAsm, } // funcOf returns a function by name and set ftype = goFunc, cFunc, etc. @@ -456,6 +469,8 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon ret = pystr(b, args) case llgoCstr: ret = cstr(b, args) + case llgoAsm: + ret = asm(b, args) case llgoCgoCString: ret = p.cgoCString(b, args) case llgoCgoCBytes: diff --git a/ssa/expr.go b/ssa/expr.go index ffebb888..6f98cb38 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -275,6 +275,17 @@ func (b Builder) CBytes(v Expr) Expr { return b.Call(fn, v) } +// InlineAsm generates inline assembly instruction +func (b Builder) InlineAsm(instruction string) { + if debugInstr { + log.Printf("InlineAsm %s\n", instruction) + } + + typ := llvm.FunctionType(b.Prog.tyVoid(), nil, false) + asm := llvm.InlineAsm(typ, instruction, "", true, false, llvm.InlineAsmDialectATT, false) + b.impl.CreateCall(typ, asm, nil, "") +} + // GoString returns a Go string func (b Builder) GoString(v Expr) Expr { fn := b.Pkg.rtFunc("GoString")