diff --git a/ir.go b/ir.go index be27442..9d471a4 100644 --- a/ir.go +++ b/ir.go @@ -67,6 +67,9 @@ type ( Attribute struct { C C.LLVMAttributeRef } + OperandBundle struct { + C C.LLVMOperandBundleRef + } Opcode C.LLVMOpcode AtomicRMWBinOp C.LLVMAtomicRMWBinOp AtomicOrdering C.LLVMAtomicOrdering @@ -92,6 +95,7 @@ func (c PassManager) IsNil() bool { return c.C == nil } func (c Use) IsNil() bool { return c.C == nil } func (c Attribute) IsNil() bool { return c.C == nil } func (c Metadata) IsNil() bool { return c.C == nil } +func (c OperandBundle) IsNil() bool { return c.C == nil } // helpers func llvmTypeRefPtr(t *Type) *C.LLVMTypeRef { return (*C.LLVMTypeRef)(unsafe.Pointer(t)) } @@ -99,6 +103,9 @@ func llvmValueRefPtr(t *Value) *C.LLVMValueRef { return (*C.LLVMValueRef)(unsafe func llvmMetadataRefPtr(t *Metadata) *C.LLVMMetadataRef { return (*C.LLVMMetadataRef)(unsafe.Pointer(t)) } +func llvmOperandBundleRefPtr(t *OperandBundle) *C.LLVMOperandBundleRef { + return (*C.LLVMOperandBundleRef)(unsafe.Pointer(t)) +} func llvmBasicBlockRefPtr(t *BasicBlock) *C.LLVMBasicBlockRef { return (*C.LLVMBasicBlockRef)(unsafe.Pointer(t)) } @@ -145,6 +152,27 @@ func llvmMetadataRefs(mds []Metadata) (*C.LLVMMetadataRef, C.unsigned) { return pt, ptlen } +func llvmOperandBundleRefs(bundles []OperandBundle) (*C.LLVMOperandBundleRef, C.unsigned) { + var pt *C.LLVMOperandBundleRef + ptlen := C.unsigned(len(bundles)) + if ptlen > 0 { + pt = llvmOperandBundleRefPtr(&bundles[0]) + } + return pt, ptlen +} + +func NewOperandBundle(tag string, args []Value) (bundle OperandBundle) { + ctag := C.CString(tag) + defer C.free(unsafe.Pointer(ctag)) + values, nvalues := llvmValueRefs(args) + bundle.C = C.LLVMCreateOperandBundle(ctag, C.size_t(len(tag)), values, nvalues) + return +} + +func (bundle OperandBundle) Dispose() { + C.LLVMDisposeOperandBundle(bundle.C) +} + //------------------------------------------------------------------------- // llvm.Opcode //------------------------------------------------------------------------- @@ -1913,6 +1941,16 @@ func (b Builder) CreateCall(t Type, fn Value, args []Value, name string) (v Valu return } +func (b Builder) CreateCallWithOperandBundles(t Type, fn Value, args []Value, bundles []OperandBundle, name string) (v Value) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + values, nvalues := llvmValueRefs(args) + operandBundles, nbundles := llvmOperandBundleRefs(bundles) + v.C = C.LLVMBuildCallWithOperandBundles(b.C, t.C, fn.C, values, nvalues, + operandBundles, nbundles, cname) + return +} + func (b Builder) CreateSelect(ifv, thenv, elsev Value, name string) (v Value) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) diff --git a/ir_test.go b/ir_test.go index 17ecd43..3767a11 100644 --- a/ir_test.go +++ b/ir_test.go @@ -52,6 +52,39 @@ func TestCreateCallBrIntrinsic(t *testing.T) { } } +func TestCreateCallWithOperandBundles(t *testing.T) { + ctx := NewContext() + defer ctx.Dispose() + mod := ctx.NewModule("operand-bundle") + defer mod.Dispose() + b := ctx.NewBuilder() + defer b.Dispose() + + donothingID := LookupIntrinsicID("llvm.donothing") + if donothingID == 0 { + t.Fatal("llvm.donothing intrinsic is unavailable") + } + donothing := GetIntrinsicDeclaration(mod, donothingID, nil) + fnType := FunctionType(ctx.VoidType(), []Type{PointerType(ctx.Int8Type(), 0)}, false) + fn := AddFunction(mod, "f", fnType) + entry := ctx.AddBasicBlock(fn, "entry") + + b.SetInsertPointAtEnd(entry) + bundle := NewOperandBundle("go.keepalive", []Value{fn.Param(0)}) + b.CreateCallWithOperandBundles(donothing.GlobalValueType(), donothing, nil, + []OperandBundle{bundle}, "") + bundle.Dispose() + 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, + `call void @llvm.donothing() [ "go.keepalive"(ptr %0) ]`) { + t.Fatalf("module does not contain operand bundle:\n%s", got) + } +} + func TestReplaceIncomingBlock(t *testing.T) { ctx := NewContext() defer ctx.Dispose()