diff --git a/IRBindings.cpp b/IRBindings.cpp index 4788c92..27dd35c 100644 --- a/IRBindings.cpp +++ b/IRBindings.cpp @@ -13,6 +13,7 @@ #include "IRBindings.h" #include "llvm/ADT/SmallVector.h" #include "llvm/IR/Attributes.h" +#include "llvm/IR/Constants.h" #include "llvm/IR/DebugLoc.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Function.h" @@ -27,6 +28,10 @@ LLVMMetadataRef LLVMConstantAsMetadata(LLVMValueRef C) { return wrap(ConstantAsMetadata::get(unwrap(C))); } +LLVMValueRef LLVMGoConstTokenNone(LLVMContextRef C) { + return wrap(ConstantTokenNone::get(*unwrap(C))); +} + LLVMMetadataRef LLVMMDString2(LLVMContextRef C, const char *Str, unsigned SLen) { return wrap(MDString::get(*unwrap(C), StringRef(Str, SLen))); } diff --git a/IRBindings.h b/IRBindings.h index 5428be7..b2356e4 100644 --- a/IRBindings.h +++ b/IRBindings.h @@ -35,6 +35,8 @@ struct LLVMDebugLocMetadata{ LLVMMetadataRef LLVMConstantAsMetadata(LLVMValueRef Val); +LLVMValueRef LLVMGoConstTokenNone(LLVMContextRef C); + LLVMMetadataRef LLVMMDString2(LLVMContextRef C, const char *Str, unsigned SLen); LLVMMetadataRef LLVMMDNode2(LLVMContextRef C, LLVMMetadataRef *MDs, unsigned Count); diff --git a/ir.go b/ir.go index f93d080..573c531 100644 --- a/ir.go +++ b/ir.go @@ -829,6 +829,12 @@ func (v Value) SetOperand(i int, op Value) { C.LLVMSetOperand(v.C, C.unsigned(i) func (v Value) OperandsCount() int { return int(C.LLVMGetNumOperands(v.C)) } // Operations on constants of any type +// ConstTokenNone returns the empty token constant owned by c. +func (c Context) ConstTokenNone() (v Value) { + v.C = C.LLVMGoConstTokenNone(c.C) + return +} + func ConstNull(t Type) (v Value) { v.C = C.LLVMConstNull(t.C); return } func ConstAllOnes(t Type) (v Value) { v.C = C.LLVMConstAllOnes(t.C); return } func Undef(t Type) (v Value) { v.C = C.LLVMGetUndef(t.C); return } diff --git a/ir_test.go b/ir_test.go index 0a9d5e8..cf70902 100644 --- a/ir_test.go +++ b/ir_test.go @@ -175,6 +175,77 @@ func TestIntrinsicBindings(t *testing.T) { } } +func TestConstTokenNoneWithCoroutineIntrinsics(t *testing.T) { + ctx := NewContext() + defer ctx.Dispose() + + none := ctx.ConstTokenNone() + if none.IsNil() { + t.Fatal("ConstTokenNone returned a nil value") + } + if got := none.Type(); got != ctx.TokenType() || got.TypeKind() != TokenTypeKind { + t.Fatalf("ConstTokenNone type = %v (kind %v), want token", got, got.TypeKind()) + } + if got := strings.TrimSpace(none.String()); got != "token none" { + t.Fatalf("ConstTokenNone string = %q, want %q", got, "token none") + } + + majorVersion, err := strconv.Atoi(strings.SplitN(Version, ".", 2)[0]) + if err != nil { + t.Fatalf("could not parse LLVM version: %v", err) + } + + mod := ctx.NewModule("coro-token-none") + defer mod.Dispose() + builder := ctx.NewBuilder() + defer builder.Dispose() + + ptrTy := PointerType(ctx.Int8Type(), 0) + fn := AddFunction(mod, "use_token_none", FunctionType(ctx.VoidType(), []Type{ptrTy}, false)) + builder.SetInsertPointAtEnd(ctx.AddBasicBlock(fn, "entry")) + falseValue := ConstInt(ctx.Int1Type(), 0, false) + + suspendID := LookupIntrinsicID("llvm.coro.suspend") + if suspendID == 0 { + t.Fatal("could not look up llvm.coro.suspend intrinsic") + } + suspend := builder.CreateIntrinsic(ctx.Int8Type(), suspendID, []Value{none, falseValue}, "suspend") + if suspend.IsNil() { + t.Fatal("could not construct llvm.coro.suspend with token none") + } + + // LLVM 18 added the unwind token operand to llvm.coro.end. LLVM 22 + // subsequently changed only its result type from i1 to void. + if majorVersion >= 18 { + endID := LookupIntrinsicID("llvm.coro.end") + if endID == 0 { + t.Fatal("could not look up llvm.coro.end intrinsic") + } + endType := ctx.Int1Type() + endName := "end" + if majorVersion >= 22 { + endType = ctx.VoidType() + endName = "" + } + end := builder.CreateIntrinsic(endType, endID, []Value{fn.Param(0), falseValue, none}, endName) + if end.IsNil() { + t.Fatal("could not construct llvm.coro.end with token none") + } + } + builder.CreateRetVoid() + + if err := VerifyModule(mod, ReturnStatusAction); err != nil { + t.Fatalf("module with token-none coroutine operands should verify: %v\n%s", err, mod.String()) + } + text := mod.String() + if !strings.Contains(text, "@llvm.coro.suspend(token none, i1 false)") { + t.Fatalf("llvm.coro.suspend did not print token none:\n%s", text) + } + if majorVersion >= 18 && !strings.Contains(text, "i1 false, token none)") { + t.Fatalf("llvm.coro.end did not print token none:\n%s", text) + } +} + func TestSubtypes(t *testing.T) { cont := NewContext() defer cont.Dispose()