diff --git a/ir.go b/ir.go index d9f786b..be27442 100644 --- a/ir.go +++ b/ir.go @@ -118,6 +118,24 @@ func llvmValueRefs(values []Value) (*C.LLVMValueRef, C.unsigned) { return pt, ptlen } +func llvmTypeRefs(types []Type) (*C.LLVMTypeRef, C.size_t) { + var pt *C.LLVMTypeRef + ptlen := C.size_t(len(types)) + if ptlen > 0 { + pt = llvmTypeRefPtr(&types[0]) + } + return pt, ptlen +} + +func llvmBasicBlockRefs(blocks []BasicBlock) (*C.LLVMBasicBlockRef, C.unsigned) { + var pt *C.LLVMBasicBlockRef + ptlen := C.unsigned(len(blocks)) + if ptlen > 0 { + pt = llvmBasicBlockRefPtr(&blocks[0]) + } + return pt, ptlen +} + func llvmMetadataRefs(mds []Metadata) (*C.LLVMMetadataRef, C.unsigned) { var pt *C.LLVMMetadataRef ptlen := C.unsigned(len(mds)) @@ -1075,6 +1093,19 @@ func NextFunction(v Value) (rv Value) { rv.C = C.LLVMGetNextFunction(v.C); func PrevFunction(v Value) (rv Value) { rv.C = C.LLVMGetPreviousFunction(v.C); return } func (v Value) EraseFromParentAsFunction() { C.LLVMDeleteFunction(v.C) } func (v Value) IntrinsicID() int { return int(C.LLVMGetIntrinsicID(v.C)) } + +func LookupIntrinsicID(name string) uint { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + return uint(C.LLVMLookupIntrinsicID(cname, C.size_t(len(name)))) +} + +func GetIntrinsicDeclaration(m Module, id uint, overloadTypes []Type) (v Value) { + types, ntypes := llvmTypeRefs(overloadTypes) + v.C = C.LLVMGetIntrinsicDeclaration(m.C, C.unsigned(id), types, ntypes) + return +} + func (v Value) FunctionCallConv() CallConv { return CallConv(C.LLVMCallConv(C.LLVMGetFunctionCallConv(v.C))) } @@ -1371,6 +1402,15 @@ func (b Builder) CreateIndirectBr(addr Value, numDests int) (rv Value) { rv.C = C.LLVMBuildIndirectBr(b.C, addr.C, C.unsigned(numDests)) return } +func (b Builder) CreateCallBr(t Type, fn Value, args []Value, defaultDest BasicBlock, indirectDests []BasicBlock, name string) (rv Value) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + dests, ndests := llvmBasicBlockRefs(indirectDests) + callArgs, nargs := llvmValueRefs(args) + rv.C = C.LLVMBuildCallBr(b.C, t.C, fn.C, defaultDest.C, dests, ndests, + callArgs, nargs, nil, 0, cname) + return +} func (b Builder) CreateInvoke(t Type, fn Value, args []Value, then, catch BasicBlock, name string) (rv Value) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) diff --git a/ir_test.go b/ir_test.go index 7f06988..17ecd43 100644 --- a/ir_test.go +++ b/ir_test.go @@ -18,6 +18,40 @@ import ( "testing" ) +func TestCreateCallBrIntrinsic(t *testing.T) { + ctx := NewContext() + defer ctx.Dispose() + mod := ctx.NewModule("callbr-intrinsic") + defer mod.Dispose() + b := ctx.NewBuilder() + defer b.Dispose() + + intrinsicID := LookupIntrinsicID("llvm.go.defer.edge") + if intrinsicID == 0 { + t.Fatal("llvm.go.defer.edge intrinsic is unavailable") + } + deferEdge := GetIntrinsicDeclaration(mod, intrinsicID, nil) + fn := AddFunction(mod, "f", FunctionType(ctx.VoidType(), nil, false)) + entry := ctx.AddBasicBlock(fn, "entry") + normal := ctx.AddBasicBlock(fn, "normal") + recover := ctx.AddBasicBlock(fn, "recover") + + b.SetInsertPointAtEnd(entry) + b.CreateCallBr(deferEdge.GlobalValueType(), deferEdge, nil, normal, []BasicBlock{recover}, "") + b.SetInsertPointAtEnd(normal) + b.CreateRetVoid() + b.SetInsertPointAtEnd(recover) + b.CreateRetVoid() + + if err := VerifyModule(mod, ReturnStatusAction); err != nil { + t.Fatalf("module verification failed: %v\n%s", err, mod.String()) + } + if got := mod.String(); !strings.Contains(got, "callbr void @llvm.go.defer.edge()") || + !strings.Contains(got, "to label %normal [label %recover]") { + t.Fatalf("module does not contain defer callbr:\n%s", got) + } +} + func TestReplaceIncomingBlock(t *testing.T) { ctx := NewContext() defer ctx.Dispose()