diff options
author | Ayke van Laethem <[email protected]> | 2023-09-18 17:54:29 +0200 |
---|---|---|
committer | Ayke <[email protected]> | 2023-10-01 18:32:15 +0200 |
commit | 1da1abe3147796aa56a5486ed6f07afdd88d8234 (patch) | |
tree | 256e56c96855bb27e67834a9586bac510ccff910 | |
parent | c9721197d5adb70a5086ecd320a1e3745bcaacc7 (diff) | |
download | tinygo-1da1abe3147796aa56a5486ed6f07afdd88d8234.tar.gz tinygo-1da1abe3147796aa56a5486ed6f07afdd88d8234.zip |
all: remove LLVM 14 support
This is a big change: apart from removing LLVM 14 it also removes typed
pointer support (which was only fully supported in LLVM up to version
14). This removes about 200 lines of code, but more importantly removes
a ton of special cases for LLVM 14.
33 files changed, 247 insertions, 467 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 1dff3ba71..f7458742f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -98,12 +98,12 @@ commands: - /go/pkg/mod jobs: - test-llvm14-go118: + test-llvm15-go118: docker: - image: golang:1.18-buster steps: - test-linux: - llvm: "14" + llvm: "15" resource_class: large workflows: @@ -111,4 +111,4 @@ workflows: jobs: # This tests our lowest supported versions of Go and LLVM, to make sure at # least the smoke tests still pass. - - test-llvm14-go118 + - test-llvm15-go118 diff --git a/GNUmakefile b/GNUmakefile index cf41866cc..a00eee1f9 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld # Try to autodetect LLVM build tools. # Versions are listed here in descending priority order. -LLVM_VERSIONS = 16 15 14 13 12 11 +LLVM_VERSIONS = 16 15 errifempty = $(if $(1),$(1),$(error $(2))) detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2))) toolSearchPathsVersion = $(1)-$(2) diff --git a/builder/build.go b/builder/build.go index dc360b92e..136669a1c 100644 --- a/builder/build.go +++ b/builder/build.go @@ -532,13 +532,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe irbuilder := mod.Context().NewBuilder() defer irbuilder.Dispose() irbuilder.SetInsertPointAtEnd(block) - i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) + ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) for _, pkg := range lprogram.Sorted() { pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init") if pkgInit.IsNil() { panic("init not found for " + pkg.Pkg.Path()) } - irbuilder.CreateCall(pkgInit.GlobalValueType(), pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "") + irbuilder.CreateCall(pkgInit.GlobalValueType(), pkgInit, []llvm.Value{llvm.Undef(ptrType)}, "") } irbuilder.CreateRetVoid() @@ -764,7 +764,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe if sizeLevel >= 2 { // Workaround with roughly the same effect as // https://reviews.llvm.org/D119342. - // Can hopefully be removed in LLVM 15. + // Can hopefully be removed in LLVM 18. ldflags = append(ldflags, "-mllvm", "--rotation-max-header-size=0") } diff --git a/builder/library.go b/builder/library.go index 6517355b6..5e36c384b 100644 --- a/builder/library.go +++ b/builder/library.go @@ -178,12 +178,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ if strings.HasPrefix(target, "riscv64-") { args = append(args, "-march=rv64gc") } - if strings.HasPrefix(target, "xtensa") { - // Hack to work around an issue in the Xtensa port: - // https://github.com/espressif/llvm-project/issues/52 - // Hopefully this will be fixed soon (LLVM 14). - args = append(args, "-D__ELF__") - } var once sync.Once diff --git a/builder/musl.go b/builder/musl.go index a65a920b7..6ae1fda06 100644 --- a/builder/musl.go +++ b/builder/musl.go @@ -6,12 +6,10 @@ import ( "os" "path/filepath" "regexp" - "strconv" "strings" "github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/goenv" - "tinygo.org/x/go-llvm" ) var Musl = Library{ @@ -93,6 +91,7 @@ var Musl = Library{ "-Wno-string-plus-int", "-Wno-ignored-pragmas", "-Wno-tautological-constant-out-of-range-compare", + "-Wno-deprecated-non-prototype", "-Qunused-arguments", // Select include dirs. Don't include standard library includes // (that would introduce host dependencies and other complications), @@ -106,11 +105,6 @@ var Musl = Library{ "-I" + muslDir + "/include", "-fno-stack-protector", } - llvmMajor, _ := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0]) - if llvmMajor >= 15 { - // This flag was added in Clang 15. It is not present in LLVM 14. - cflags = append(cflags, "-Wno-deprecated-non-prototype") - } return cflags }, sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") }, diff --git a/cgo/libclang_config_llvm14.go b/cgo/libclang_config_llvm14.go deleted file mode 100644 index 861ced9de..000000000 --- a/cgo/libclang_config_llvm14.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build !byollvm && llvm14 - -package cgo - -/* -#cgo linux CFLAGS: -I/usr/lib/llvm-14/include -#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@14/include -#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@14/include -#cgo freebsd CFLAGS: -I/usr/local/llvm14/include -#cgo linux LDFLAGS: -L/usr/lib/llvm-14/lib -lclang -#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@14/lib -lclang -lffi -#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@14/lib -lclang -lffi -#cgo freebsd LDFLAGS: -L/usr/local/llvm14/lib -lclang -*/ -import "C" diff --git a/cgo/libclang_config_llvm16.go b/cgo/libclang_config_llvm16.go index 79aacd2f2..01c3a7e2d 100644 --- a/cgo/libclang_config_llvm16.go +++ b/cgo/libclang_config_llvm16.go @@ -1,4 +1,4 @@ -//go:build !byollvm && !llvm14 && !llvm15 +//go:build !byollvm && !llvm15 package cgo diff --git a/compiler/atomic.go b/compiler/atomic.go index 48a9fb2d2..006da5ef8 100644 --- a/compiler/atomic.go +++ b/compiler/atomic.go @@ -35,17 +35,7 @@ func (b *builder) createAtomicOp(name string) llvm.Value { case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer": ptr := b.getValue(b.fn.Params[0], getPos(b.fn)) val := b.getValue(b.fn.Params[1], getPos(b.fn)) - isPointer := val.Type().TypeKind() == llvm.PointerTypeKind - if isPointer { - // atomicrmw only supports integers, so cast to an integer. - // TODO: this is fixed in LLVM 15. - val = b.CreatePtrToInt(val, b.uintptrType, "") - ptr = b.CreateBitCast(ptr, llvm.PointerType(val.Type(), 0), "") - } oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpXchg, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true) - if isPointer { - oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "") - } return oldVal case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer": ptr := b.getValue(b.fn.Params[0], getPos(b.fn)) @@ -63,24 +53,6 @@ func (b *builder) createAtomicOp(name string) llvm.Value { case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer": ptr := b.getValue(b.fn.Params[0], getPos(b.fn)) val := b.getValue(b.fn.Params[1], getPos(b.fn)) - if strings.HasPrefix(b.Triple, "avr") { - // SelectionDAGBuilder is currently missing the "are unaligned atomics allowed" check for stores. - vType := val.Type() - isPointer := vType.TypeKind() == llvm.PointerTypeKind - if isPointer { - // libcalls only supports integers, so cast to an integer. - vType = b.uintptrType - val = b.CreatePtrToInt(val, vType, "") - ptr = b.CreateBitCast(ptr, llvm.PointerType(vType, 0), "") - } - name := fmt.Sprintf("__atomic_store_%d", vType.IntTypeWidth()/8) - fn := b.mod.NamedFunction(name) - if fn.IsNil() { - fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType, b.uintptrType}, false)) - } - b.createCall(fn.GlobalValueType(), fn, []llvm.Value{ptr, val, llvm.ConstInt(b.uintptrType, 5, false)}, "") - return llvm.Value{} - } store := b.CreateStore(val, ptr) store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent) store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required diff --git a/compiler/calls.go b/compiler/calls.go index a110addcf..f4b76a513 100644 --- a/compiler/calls.go +++ b/compiler/calls.go @@ -45,7 +45,7 @@ func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name if llvmFn.IsNil() { panic("trying to call non-existent function: " + fn.RelString(nil)) } - args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter + args = append(args, llvm.Undef(b.dataPtrType)) // unused context parameter if isInvoke { return b.createInvoke(fnType, llvmFn, args, name) } diff --git a/compiler/channel.go b/compiler/channel.go index c8c10fe0b..9969835e8 100644 --- a/compiler/channel.go +++ b/compiler/channel.go @@ -33,28 +33,27 @@ func (b *builder) createChanSend(instr *ssa.Send) { // store value-to-send valueType := b.getLLVMType(instr.X.Type()) isZeroSize := b.targetData.TypeAllocSize(valueType) == 0 - var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value + var valueAlloca, valueAllocaSize llvm.Value if isZeroSize { - valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0)) - valueAllocaCast = llvm.ConstNull(b.i8ptrType) + valueAlloca = llvm.ConstNull(b.dataPtrType) } else { - valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value") + valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value") b.CreateStore(chanValue, valueAlloca) } // Allocate blockedlist buffer. channelBlockedList := b.getLLVMRuntimeType("channelBlockedList") - channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList") + channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList") // Do the send. - b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "") + b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "") // End the lifetime of the allocas. // This also works around a bug in CoroSplit, at least in LLVM 8: // https://bugs.llvm.org/show_bug.cgi?id=41742 - b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize) + b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize) if !isZeroSize { - b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) + b.emitLifetimeEnd(valueAlloca, valueAllocaSize) } } @@ -66,28 +65,27 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value { // Allocate memory to receive into. isZeroSize := b.targetData.TypeAllocSize(valueType) == 0 - var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value + var valueAlloca, valueAllocaSize llvm.Value if isZeroSize { - valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0)) - valueAllocaCast = llvm.ConstNull(b.i8ptrType) + valueAlloca = llvm.ConstNull(b.dataPtrType) } else { - valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value") + valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value") } // Allocate blockedlist buffer. channelBlockedList := b.getLLVMRuntimeType("channelBlockedList") - channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList") + channelBlockedListAlloca, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList") // Do the receive. - commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "") + commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelBlockedListAlloca}, "") var received llvm.Value if isZeroSize { received = llvm.ConstNull(valueType) } else { received = b.CreateLoad(valueType, valueAlloca, "chan.received") - b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) + b.emitLifetimeEnd(valueAlloca, valueAllocaSize) } - b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize) + b.emitLifetimeEnd(channelBlockedListAlloca, channelBlockedListAllocaSize) if unop.CommaOk { tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false)) @@ -159,8 +157,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value { sendValue := b.getValue(state.Send, state.Pos) alloca := llvmutil.CreateEntryBlockAlloca(b.Builder, sendValue.Type(), "select.send.value") b.CreateStore(sendValue, alloca) - ptr := b.CreateBitCast(alloca, b.i8ptrType, "") - selectState = b.CreateInsertValue(selectState, ptr, 1, "") + selectState = b.CreateInsertValue(selectState, alloca, 1, "") default: panic("unreachable") } @@ -168,10 +165,10 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value { } // Create a receive buffer, where the received value will be stored. - recvbuf := llvm.Undef(b.i8ptrType) + recvbuf := llvm.Undef(b.dataPtrType) if recvbufSize != 0 { allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize)) - recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca") + recvbufAlloca, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca") recvbufAlloca.SetAlignment(recvbufAlign) recvbuf = b.CreateGEP(allocaType, recvbufAlloca, []llvm.Value{ llvm.ConstInt(b.ctx.Int32Type(), 0, false), @@ -181,7 +178,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value { // Create the states slice (allocated on the stack). statesAllocaType := llvm.ArrayType(chanSelectStateType, len(selectStates)) - statesAlloca, statesI8, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca") + statesAlloca, statesSize := b.createTemporaryAlloca(statesAllocaType, "select.states.alloca") for i, state := range selectStates { // Set each slice element to the appropriate channel. gep := b.CreateGEP(statesAllocaType, statesAlloca, []llvm.Value{ @@ -202,7 +199,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value { // Stack-allocate operation structures. // If these were simply created as a slice, they would heap-allocate. chBlockAllocaType := llvm.ArrayType(b.getLLVMRuntimeType("channelBlockedList"), len(selectStates)) - chBlockAlloca, chBlockAllocaPtr, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca") + chBlockAlloca, chBlockSize := b.createTemporaryAlloca(chBlockAllocaType, "select.block.alloca") chBlockLen := llvm.ConstInt(b.uintptrType, uint64(len(selectStates)), false) chBlockPtr := b.CreateGEP(chBlockAllocaType, chBlockAlloca, []llvm.Value{ llvm.ConstInt(b.ctx.Int32Type(), 0, false), @@ -216,7 +213,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value { }, "select.result") // Terminate the lifetime of the operation structures. - b.emitLifetimeEnd(chBlockAllocaPtr, chBlockSize) + b.emitLifetimeEnd(chBlockAlloca, chBlockSize) } else { results = b.createRuntimeCall("tryChanSelect", []llvm.Value{ recvbuf, @@ -225,7 +222,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value { } // Terminate the lifetime of the states alloca. - b.emitLifetimeEnd(statesI8, statesSize) + b.emitLifetimeEnd(statesAlloca, statesSize) // The result value does not include all the possible received values, // because we can't load them in advance. Instead, the *ssa.Extract @@ -265,7 +262,6 @@ func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value { // it to the correct type, and dereference it. recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)] typ := b.getLLVMType(expr.Type()) - ptr := b.CreateBitCast(recvbuf, llvm.PointerType(typ, 0), "") - return b.CreateLoad(typ, ptr, "") + return b.CreateLoad(typ, recvbuf, "") } } diff --git a/compiler/compiler.go b/compiler/compiler.go index cb3a892a8..7a1c1e440 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -75,10 +75,9 @@ type compilerContext struct { machine llvm.TargetMachine targetData llvm.TargetData intType llvm.Type - i8ptrType llvm.Type // for convenience - rawVoidFuncType llvm.Type // for convenience + dataPtrType llvm.Type // pointer in address space 0 + funcPtrType llvm.Type // pointer in function address space (1 for AVR, 0 elsewhere) funcPtrAddrSpace int - hasTypedPointers bool // for LLVM 14 backwards compatibility uintptrType llvm.Type program *ssa.Program diagnostics []error @@ -123,13 +122,12 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C } else { panic("unknown pointer size") } - c.i8ptrType = llvm.PointerType(c.ctx.Int8Type(), 0) + c.dataPtrType = llvm.PointerType(c.ctx.Int8Type(), 0) dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false) dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType) c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace() - c.hasTypedPointers = c.i8ptrType != llvm.PointerType(c.ctx.Int16Type(), 0) // with opaque pointers, all pointers are the same type (LLVM 15+) - c.rawVoidFuncType = dummyFunc.Type() + c.funcPtrType = dummyFunc.Type() dummyFunc.EraseFromParentAsFunction() return c @@ -417,16 +415,14 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type { case types.Uintptr: return c.uintptrType case types.UnsafePointer: - return c.i8ptrType + return c.dataPtrType default: panic("unknown basic type: " + typ.String()) } - case *types.Chan: - return llvm.PointerType(c.getLLVMRuntimeType("channel"), 0) + case *types.Chan, *types.Map, *types.Pointer: + return c.dataPtrType // all pointers are the same case *types.Interface: return c.getLLVMRuntimeType("_interface") - case *types.Map: - return llvm.PointerType(c.getLLVMRuntimeType("hashmap"), 0) case *types.Named: if st, ok := typ.Underlying().(*types.Struct); ok { // Structs are a special case. While other named types are ignored @@ -441,21 +437,11 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type { return llvmType } return c.getLLVMType(typ.Underlying()) - case *types.Pointer: - if c.hasTypedPointers { - ptrTo := c.getLLVMType(typ.Elem()) - return llvm.PointerType(ptrTo, 0) - } - return c.i8ptrType // all pointers are the same case *types.Signature: // function value return c.getFuncType(typ) case *types.Slice: - ptrType := c.i8ptrType - if c.hasTypedPointers { - ptrType = llvm.PointerType(c.getLLVMType(typ.Elem()), 0) - } members := []llvm.Type{ - ptrType, + c.dataPtrType, c.uintptrType, // len c.uintptrType, // cap } @@ -545,8 +531,8 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata { Elements: []llvm.Metadata{ c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{ Name: "ptr", - SizeInBits: c.targetData.TypeAllocSize(c.i8ptrType) * 8, - AlignInBits: uint32(c.targetData.ABITypeAlignment(c.i8ptrType)) * 8, + SizeInBits: c.targetData.TypeAllocSize(c.dataPtrType) * 8, + AlignInBits: uint32(c.targetData.ABITypeAlignment(c.dataPtrType)) * 8, OffsetInBits: 0, Type: c.getDIType(types.NewPointer(types.Typ[types.Byte])), }), @@ -1548,21 +1534,18 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c src := argValues[0] elems := argValues[1] srcBuf := b.CreateExtractValue(src, 0, "append.srcBuf") - srcPtr := b.CreateBitCast(srcBuf, b.i8ptrType, "append.srcPtr") srcLen := b.CreateExtractValue(src, 1, "append.srcLen") srcCap := b.CreateExtractValue(src, 2, "append.srcCap") elemsBuf := b.CreateExtractValue(elems, 0, "append.elemsBuf") - elemsPtr := b.CreateBitCast(elemsBuf, b.i8ptrType, "append.srcPtr") elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen") elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem()) elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false) - result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcPtr, elemsPtr, srcLen, srcCap, elemsLen, elemSize}, "append.new") + result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize}, "append.new") newPtr := b.CreateExtractValue(result, 0, "append.newPtr") - newBuf := b.CreateBitCast(newPtr, srcBuf.Type(), "append.newBuf") newLen := b.CreateExtractValue(result, 1, "append.newLen") newCap := b.CreateExtractValue(result, 2, "append.newCap") newSlice := llvm.Undef(src.Type()) - newSlice = b.CreateInsertValue(newSlice, newBuf, 0, "") + newSlice = b.CreateInsertValue(newSlice, newPtr, 0, "") newSlice = b.CreateInsertValue(newSlice, newLen, 1, "") newSlice = b.CreateInsertValue(newSlice, newCap, 2, "") return newSlice, nil @@ -1610,9 +1593,6 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c // The pointer to the data to be cleared. llvmBuf := b.CreateExtractValue(value, 0, "buf") - if llvmBuf.Type() != b.i8ptrType { // compatibility with LLVM 14 - llvmBuf = b.CreateBitCast(llvmBuf, b.i8ptrType, "") - } // The length (in bytes) to be cleared. llvmLen := b.CreateExtractValue(value, 1, "len") @@ -1647,8 +1627,6 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray") srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray") elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem()) - dstBuf = b.CreateBitCast(dstBuf, b.i8ptrType, "copy.dstPtr") - srcBuf = b.CreateBitCast(srcBuf, b.i8ptrType, "copy.srcPtr") elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false) return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil case "delete": @@ -1888,14 +1866,13 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) switch value := instr.Value.(type) { case *ssa.Function: // Regular function call. No context is necessary. - context = llvm.Undef(b.i8ptrType) + context = llvm.Undef(b.dataPtrType) if info.variadic && len(fn.Params) == 0 { // This matches Clang, see: https://godbolt.org/z/Gqv49xKMq // Eventually we might be able to eliminate this special case // entirely. For details, see: // https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521 calleeType = llvm.FunctionType(callee.GlobalValueType().ReturnType(), nil, false) - callee = llvm.ConstBitCast(callee, llvm.PointerType(calleeType, b.funcPtrAddrSpace)) } case *ssa.MakeClosure: // A call on a func value, but the callee is trivial to find. For @@ -1923,13 +1900,14 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) params = append(params, typecode) callee = b.getInvokeFunction(instr) calleeType = callee.GlobalValueType() - context = llvm.Undef(b.i8ptrType) + context = llvm.Undef(b.dataPtrType) } else { // Function pointer. value := b.getValue(instr.Value, getPos(instr)) // This is a func value, which cannot be called directly. We have to // extract the function pointer and context first from the func value. - calleeType, callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature)) + callee, context = b.decodeFuncValue(value) + calleeType = b.getLLVMFunctionType(instr.Value.Type().Underlying().(*types.Signature)) b.createNilCheck(instr.Value, callee, "fpcall") } @@ -1962,7 +1940,7 @@ func (b *builder) getValue(expr ssa.Value, pos token.Pos) llvm.Value { return llvm.Undef(b.getLLVMType(expr.Type())) } _, fn := b.getFunction(expr) - return b.createFuncValue(fn, llvm.Undef(b.i8ptrType), expr.Signature) + return b.createFuncValue(fn, llvm.Undef(b.dataPtrType), expr.Signature) case *ssa.Global: value := b.getGlobal(expr) if value.IsNil() { @@ -2030,7 +2008,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) { sizeValue := llvm.ConstInt(b.uintptrType, size, false) layoutValue := b.createObjectLayout(typ, expr.Pos()) buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment) - buf = b.CreateBitCast(buf, llvm.PointerType(typ, 0), "") return buf, nil } else { buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment) @@ -2076,10 +2053,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) { value = b.CreateInsertValue(value, field, i, "changetype.struct") } return value, nil - case llvm.PointerTypeKind: - // This can happen with pointers to structs. This case is easy: - // simply bitcast the pointer to the destination type. - return b.CreateBitCast(x, llvmType, "changetype.pointer"), nil default: return llvm.Value{}, errors.New("todo: unknown ChangeType type: " + expr.X.Type().String()) } @@ -2156,12 +2129,12 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) { // Can't load directly from array (as index is non-constant), so // have to do it using an alloca+gep+load. arrayType := collection.Type() - alloca, allocaPtr, allocaSize := b.createTemporaryAlloca(arrayType, "index.alloca") + alloca, allocaSize := b.createTemporaryAlloca(arrayType, "index.alloca") b.CreateStore(collection, alloca) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) ptr := b.CreateInBoundsGEP(arrayType, alloca, []llvm.Value{zero, index}, "index.gep") result := b.CreateLoad(arrayType.ElementType(), ptr, "index.load") - b.emitLifetimeEnd(allocaPtr, allocaSize) + b.emitLifetimeEnd(alloca, allocaSize) return result, nil default: panic("unknown *ssa.Index type") @@ -2264,7 +2237,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) { sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap") layoutValue := b.createObjectLayout(llvmElemType, expr.Pos()) slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf") - slicePtr = b.CreateBitCast(slicePtr, llvm.PointerType(llvmElemType, 0), "makeslice.array") // Extend or truncate if necessary. This is safe as we've already done // the bounds check. @@ -2310,7 +2282,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) { default: panic("unknown type in range: " + typ.String()) } - it, _, _ := b.createTemporaryAlloca(iteratorType, "range.it") + it, _ := b.createTemporaryAlloca(iteratorType, "range.it") b.CreateStore(llvm.ConstNull(iteratorType), it) return it, nil case *ssa.Select: @@ -2478,7 +2450,6 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) { arrayLen := expr.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len() b.createSliceToArrayPointerCheck(sliceLen, arrayLen) ptr := b.CreateExtractValue(slice, 0, "") - ptr = b.CreateBitCast(ptr, b.getLLVMType(expr.Type()), "") return ptr, nil case *ssa.TypeAssert: return b.createTypeAssert(expr), nil @@ -2944,16 +2915,16 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) strPtr = llvm.ConstInBoundsGEP(globalType, global, []llvm.Value{zero, zero}) } else { - strPtr = llvm.ConstNull(c.i8ptrType) + strPtr = llvm.ConstNull(c.dataPtrType) } strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen}) return strObj } else if typ.Kind() == types.UnsafePointer { if !expr.IsNil() { value, _ := constant.Uint64Val(constant.ToInt(expr.Value)) - return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.i8ptrType) + return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.dataPtrType) } - return llvm.ConstNull(c.i8ptrType) + return llvm.ConstNull(c.dataPtrType) } else if typ.Info()&types.IsUnsigned != 0 { n, _ := constant.Uint64Val(constant.ToInt(expr.Value)) return llvm.ConstInt(llvmType, n, false) @@ -2997,7 +2968,7 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value // Create a generic nil interface with no dynamic type (typecode=0). fields := []llvm.Value{ llvm.ConstInt(c.uintptrType, 0, false), - llvm.ConstPointerNull(c.i8ptrType), + llvm.ConstPointerNull(c.dataPtrType), } return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields) case *types.Pointer: @@ -3014,8 +2985,7 @@ func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value if expr.Value != nil { panic("expected nil slice constant") } - elemType := c.getLLVMType(typ.Elem()) - llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0)) + llvmPtr := llvm.ConstPointerNull(c.dataPtrType) llvmLen := llvm.ConstInt(c.uintptrType, 0, false) slice := c.ctx.ConstStruct([]llvm.Value{ llvmPtr, // backing array @@ -3056,7 +3026,7 @@ func (b *builder) createConvert(typeFrom, typeTo types.Type, value llvm.Value, p // Conversion between pointers and unsafe.Pointer. if isPtrFrom && isPtrTo { - return b.CreateBitCast(value, llvmTypeTo, ""), nil + return value, nil } switch typeTo := typeTo.Underlying().(type) { @@ -3295,7 +3265,7 @@ func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) { if fn.IsNil() { return llvm.Value{}, b.makeError(unop.Pos(), "cgo function not found: "+name) } - return b.CreateBitCast(fn, b.i8ptrType, ""), nil + return fn, nil } else { b.createNilCheck(unop.X, x, "deref") load := b.CreateLoad(valueType, x, "") diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index 92ce31b01..fcb2dd7e2 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -169,9 +169,9 @@ func filterIrrelevantIRLines(lines []string) []string { if strings.HasPrefix(line, "source_filename = ") { continue } - if llvmVersion < 14 && strings.HasPrefix(line, "target datalayout = ") { + if llvmVersion < 15 && strings.HasPrefix(line, "target datalayout = ") { // The datalayout string may vary betewen LLVM versions. - // Right now test outputs are for LLVM 14 and higher. + // Right now test outputs are for LLVM 15 and higher. continue } out = append(out, line) diff --git a/compiler/defer.go b/compiler/defer.go index 191b64b91..e1ff2f58e 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -60,9 +60,8 @@ func (b *builder) deferInitFunc() { b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin) // Create defer list pointer. - deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0) - b.deferPtr = b.CreateAlloca(deferType, "deferPtr") - b.CreateStore(llvm.ConstPointerNull(deferType), b.deferPtr) + b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr") + b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), b.deferPtr) if b.hasDeferFrame() { // Set up the defer frame with the current stack pointer. @@ -249,8 +248,7 @@ func isInLoop(start *ssa.BasicBlock) bool { func (b *builder) createDefer(instr *ssa.Defer) { // The pointer to the previous defer struct, which we will replace to // make a linked list. - deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0) - next := b.CreateLoad(deferType, b.deferPtr, "defer.next") + next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next") var values []llvm.Value valueTypes := []llvm.Type{b.uintptrType, next.Type()} @@ -271,7 +269,7 @@ func (b *builder) createDefer(instr *ssa.Defer) { typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode") receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver") values = []llvm.Value{callback, next, typecode, receiverValue} - valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType) + valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType) for _, arg := range instr.Call.Args { val := b.getValue(arg, getPos(instr)) values = append(values, val) @@ -391,9 +389,8 @@ func (b *builder) createDefer(instr *ssa.Defer) { // This may be hit a variable number of times, so use a heap allocation. size := b.targetData.TypeAllocSize(deferredCallType) sizeValue := llvm.ConstInt(b.uintptrType, size, false) - nilPtr := llvm.ConstNull(b.i8ptrType) - allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call") - alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferredCallType, 0), "defer.alloc") + nilPtr := llvm.ConstNull(b.dataPtrType) + alloca = b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call") } if b.NeedsStackObjects { b.trackPointer(alloca) @@ -401,14 +398,12 @@ func (b *builder) createDefer(instr *ssa.Defer) { b.CreateStore(deferredCall, alloca) // Push it on top of the linked list by replacing deferPtr. - allocaCast := b.CreateBitCast(alloca, next.Type(), "defer.alloca.cast") - b.CreateStore(allocaCast, b.deferPtr) + b.CreateStore(alloca, b.deferPtr) } // createRunDefers emits code to run all deferred functions. func (b *builder) createRunDefers() { deferType := b.getLLVMRuntimeType("_defer") - deferPtrType := llvm.PointerType(deferType, 0) // Add a loop like the following: // for stack != nil { @@ -435,7 +430,7 @@ func (b *builder) createRunDefers() { // Create loop head: // for stack != nil { b.SetInsertPointAtEnd(loophead) - deferData := b.CreateLoad(deferPtrType, b.deferPtr, "") + deferData := b.CreateLoad(b.dataPtrType, b.deferPtr, "") stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil") b.CreateCondBr(stackIsNil, end, loop) @@ -448,7 +443,7 @@ func (b *builder) createRunDefers() { llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field }, "stack.next.gep") - nextStack := b.CreateLoad(deferPtrType, nextStackGEP, "stack.next") + nextStack := b.CreateLoad(b.dataPtrType, nextStackGEP, "stack.next") b.CreateStore(nextStack, b.deferPtr) gep := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{ llvm.ConstInt(b.ctx.Int32Type(), 0, false), @@ -469,28 +464,26 @@ func (b *builder) createRunDefers() { // Call on an value or interface value. // Get the real defer struct type and cast to it. - valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} + valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType} if !callback.IsInvoke() { //Expect funcValue to be passed through the deferred call. valueTypes = append(valueTypes, b.getFuncType(callback.Signature())) } else { //Expect typecode - valueTypes = append(valueTypes, b.i8ptrType, b.i8ptrType) + valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType) } for _, arg := range callback.Args { valueTypes = append(valueTypes, b.getLLVMType(arg.Type())) } - deferredCallType := b.ctx.StructType(valueTypes, false) - deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall") - // Extract the params from the struct (including receiver). forwardParams := []llvm.Value{} zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) + deferredCallType := b.ctx.StructType(valueTypes, false) for i := 2; i < len(valueTypes); i++ { - gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep") + gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep") forwardParam := b.CreateLoad(valueTypes[i], gep, "param") forwardParams = append(forwardParams, forwardParam) } @@ -505,7 +498,8 @@ func (b *builder) createRunDefers() { //Get function pointer and context var context llvm.Value - fnType, fnPtr, context = b.decodeFuncValue(funcValue, callback.Signature()) + fnPtr, context = b.decodeFuncValue(funcValue) + fnType = b.getLLVMFunctionType(callback.Signature()) //Pass context forwardParams = append(forwardParams, context) @@ -519,7 +513,7 @@ func (b *builder) createRunDefers() { // Add the context parameter. An interface call cannot also be a // closure but we have to supply the parameter anyway for platforms // with a strict calling convention. - forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) + forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType)) } b.createCall(fnType, fnPtr, forwardParams, "") @@ -528,18 +522,17 @@ func (b *builder) createRunDefers() { // Direct call. // Get the real defer struct type and cast to it. - valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} + valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType} for _, param := range getParams(callback.Signature) { valueTypes = append(valueTypes, b.getLLVMType(param.Type())) } deferredCallType := b.ctx.StructType(valueTypes, false) - deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall") // Extract the params from the struct. forwardParams := []llvm.Value{} zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) for i := range getParams(callback.Signature) { - gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") + gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param") forwardParams = append(forwardParams, forwardParam) } @@ -549,7 +542,7 @@ func (b *builder) createRunDefers() { if !b.getFunctionInfo(callback).exported { // Add the context parameter. We know it is ignored by the receiving // function, but we have to pass one anyway. - forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) + forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType)) } // Call real function. @@ -559,20 +552,19 @@ func (b *builder) createRunDefers() { case *ssa.MakeClosure: // Get the real defer struct type and cast to it. fn := callback.Fn.(*ssa.Function) - valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} + valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType} params := fn.Signature.Params() for i := 0; i < params.Len(); i++ { valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type())) } - valueTypes = append(valueTypes, b.i8ptrType) // closure + valueTypes = append(valueTypes, b.dataPtrType) // closure deferredCallType := b.ctx.StructType(valueTypes, false) - deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall") // Extract the params from the struct. forwardParams := []llvm.Value{} zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) for i := 2; i < len(valueTypes); i++ { - gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") + gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") forwardParam := b.CreateLoad(valueTypes[i], gep, "param") forwardParams = append(forwardParams, forwardParam) } @@ -584,7 +576,7 @@ func (b *builder) createRunDefers() { db := b.deferBuiltinFuncs[callback] //Get parameter types - valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} + valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType} //Get signature from call results params := callback.Type().Underlying().(*types.Signature).Params() @@ -593,13 +585,12 @@ func (b *builder) createRunDefers() { } deferredCallType := b.ctx.StructType(valueTypes, false) - deferredCallPtr := b.CreateBitCast(deferData, llvm.PointerType(deferredCallType, 0), "defercall") // Extract the params from the struct. var argValues []llvm.Value zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) for i := 0; i < params.Len(); i++ { - gep := b.CreateInBoundsGEP(deferredCallType, deferredCallPtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") + gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param") argValues = append(argValues, forwardParam) } diff --git a/compiler/func.go b/compiler/func.go index 3ac42e749..0af81445c 100644 --- a/compiler/func.go +++ b/compiler/func.go @@ -13,35 +13,14 @@ import ( // createFuncValue creates a function value from a raw function pointer with no // context. func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value { - return b.compilerContext.createFuncValue(b.Builder, funcPtr, context, sig) -} - -// createFuncValue creates a function value from a raw function pointer with no -// context. -func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context llvm.Value, sig *types.Signature) llvm.Value { // Closure is: {context, function pointer} - funcValueScalar := llvm.ConstBitCast(funcPtr, c.rawVoidFuncType) - funcValueType := c.getFuncType(sig) + funcValueType := b.getFuncType(sig) funcValue := llvm.Undef(funcValueType) - funcValue = builder.CreateInsertValue(funcValue, context, 0, "") - funcValue = builder.CreateInsertValue(funcValue, funcValueScalar, 1, "") + funcValue = b.CreateInsertValue(funcValue, context, 0, "") + funcValue = b.CreateInsertValue(funcValue, funcPtr, 1, "") return funcValue } -// getFuncSignatureID returns a new external global for a given signature. This -// global reference is not real, it is only used during func lowering to assign -// signature types to functions and will then be removed. -func (c *compilerContext) getFuncSignatureID(sig *types.Signature) llvm.Value { - s, _ := getTypeCodeName(sig) - sigGlobalName := "reflect/types.funcid:" + s - sigGlobal := c.mod.NamedGlobal(sigGlobalName) - if sigGlobal.IsNil() { - sigGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), sigGlobalName) - sigGlobal.SetGlobalConstant(true) - } - return sigGlobal -} - // extractFuncScalar returns some scalar that can be used in comparisons. It is // a cheap operation. func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value { @@ -55,28 +34,20 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value { } // decodeFuncValue extracts the context and the function pointer from this func -// value. This may be an expensive operation. -func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcType llvm.Type, funcPtr, context llvm.Value) { +// value. +func (b *builder) decodeFuncValue(funcValue llvm.Value) (funcPtr, context llvm.Value) { context = b.CreateExtractValue(funcValue, 0, "") funcPtr = b.CreateExtractValue(funcValue, 1, "") - if !funcPtr.IsAConstantExpr().IsNil() && funcPtr.Opcode() == llvm.BitCast { - funcPtr = funcPtr.Operand(0) // needed for LLVM 14 (no opaque pointers) - } - if sig != nil { - funcType = b.getRawFuncType(sig) - llvmSig := llvm.PointerType(funcType, b.funcPtrAddrSpace) - funcPtr = b.CreateBitCast(funcPtr, llvmSig, "") - } return } // getFuncType returns the type of a func value given a signature. func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type { - return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false) + return c.ctx.StructType([]llvm.Type{c.dataPtrType, c.funcPtrType}, false) } -// getRawFuncType returns a LLVM function type for a given signature. -func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type { +// getLLVMFunctionType returns a LLVM function type for a given signature. +func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type { // Get the return type. var returnType llvm.Type switch typ.Results().Len() { @@ -104,7 +75,7 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type { if recv.StructName() == "runtime._interface" { // This is a call on an interface, not a concrete type. // The receiver is not an interface, but a i8* type. - recv = c.i8ptrType + recv = c.dataPtrType } for _, info := range c.expandFormalParamType(recv, "", nil) { paramTypes = append(paramTypes, info.llvmType) @@ -117,7 +88,7 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type { } } // All functions take these parameters at the end. - paramTypes = append(paramTypes, c.i8ptrType) // context + paramTypes = append(paramTypes, c.dataPtrType) // context // Make a func type out of the signature. return llvm.FunctionType(returnType, paramTypes, false) diff --git a/compiler/gc.go b/compiler/gc.go index 0fd6f5aae..9d568a174 100644 --- a/compiler/gc.go +++ b/compiler/gc.go @@ -81,9 +81,6 @@ func (b *builder) trackValue(value llvm.Value) { // trackPointer creates a call to runtime.trackPointer, bitcasting the poitner // first if needed. The input value must be of LLVM pointer type. func (b *builder) trackPointer(value llvm.Value) { - if value.Type() != b.i8ptrType { - value = b.CreateBitCast(value, b.i8ptrType, "") - } b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "") } diff --git a/compiler/goroutine.go b/compiler/goroutine.go index 8feb5e799..95abc77ff 100644 --- a/compiler/goroutine.go +++ b/compiler/goroutine.go @@ -21,7 +21,7 @@ func (b *builder) createGo(instr *ssa.Go) { var prefix string var funcPtr llvm.Value - var funcPtrType llvm.Type + var funcType llvm.Type hasContext := false if callee := instr.Call.StaticCallee(); callee != nil { // Static callee is known. This makes it easier to start a new @@ -42,7 +42,7 @@ func (b *builder) createGo(instr *ssa.Go) { params = append(params, context) // context parameter hasContext = true } - funcPtrType, funcPtr = b.getFunction(callee) + funcType, funcPtr = b.getFunction(callee) } else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok { // We cheat. None of the builtins do any long or blocking operation, so // we might as well run these builtins right away without the program @@ -80,7 +80,7 @@ func (b *builder) createGo(instr *ssa.Go) { itfTypeCode := b.CreateExtractValue(itf, 0, "") itfValue := b.CreateExtractValue(itf, 1, "") funcPtr = b.getInvokeFunction(&instr.Call) - funcPtrType = funcPtr.GlobalValueType() + funcType = funcPtr.GlobalValueType() params = append([]llvm.Value{itfValue}, params...) // start with receiver params = append(params, itfTypeCode) // end with typecode } else { @@ -90,7 +90,8 @@ func (b *builder) createGo(instr *ssa.Go) { // * The function context, for closures. // * The function pointer (for tasks). var context llvm.Value - funcPtrType, funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr)), instr.Call.Value.Type().Underlying().(*types.Signature)) + funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr))) + funcType = b.getLLVMFunctionType(instr.Call.Value.Type().Underlying().(*types.Signature)) params = append(params, context, funcPtr) hasContext = true prefix = b.fn.RelString(nil) @@ -98,14 +99,14 @@ func (b *builder) createGo(instr *ssa.Go) { paramBundle := b.emitPointerPack(params) var stackSize llvm.Value - callee := b.createGoroutineStartWrapper(funcPtrType, funcPtr, prefix, hasContext, instr.Pos()) + callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, instr.Pos()) if b.AutomaticStackSize { // The stack size is not known until after linking. Call a dummy // function that will be replaced with a load from a special ELF // section that contains the stack size (and is modified after // linking). stackSizeFnType, stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function)) - stackSize = b.createCall(stackSizeFnType, stackSizeFn, []llvm.Value{callee, llvm.Undef(b.i8ptrType)}, "stacksize") + stackSize = b.createCall(stackSizeFnType, stackSizeFn, []llvm.Value{callee, llvm.Undef(b.dataPtrType)}, "stacksize") } else { // The stack size is fixed at compile time. By emitting it here as a // constant, it can be optimized. @@ -115,7 +116,7 @@ func (b *builder) createGo(instr *ssa.Go) { stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false) } fnType, start := b.getFunction(b.program.ImportedPackage("internal/task").Members["start"].(*ssa.Function)) - b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType)}, "") + b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.dataPtrType)}, "") } // createGoroutineStartWrapper creates a wrapper for the task-based @@ -165,7 +166,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm. } // Create the wrapper. - wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false) + wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false) wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType) c.addStandardAttributes(wrapper) wrapper.SetLinkage(llvm.LinkOnceODRLinkage) @@ -203,7 +204,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm. } params := b.emitPointerUnpack(wrapper.Param(0), paramTypes) if !hasContext { - params = append(params, llvm.Undef(c.i8ptrType)) // add dummy context parameter + params = append(params, llvm.Undef(c.dataPtrType)) // add dummy context parameter } // Create the call. @@ -211,7 +212,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm. if c.Scheduler == "asyncify" { b.CreateCall(deadlockType, deadlock, []llvm.Value{ - llvm.Undef(c.i8ptrType), + llvm.Undef(c.dataPtrType), }, "") } @@ -234,7 +235,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm. // merged into one. // Create the wrapper. - wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false) + wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType}, false) wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType) c.addStandardAttributes(wrapper) wrapper.SetLinkage(llvm.LinkOnceODRLinkage) @@ -279,7 +280,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm. if c.Scheduler == "asyncify" { b.CreateCall(deadlockType, deadlock, []llvm.Value{ - llvm.Undef(c.i8ptrType), + llvm.Undef(c.dataPtrType), }, "") } } diff --git a/compiler/inlineasm.go b/compiler/inlineasm.go index 72dd68cf3..5e54b3be6 100644 --- a/compiler/inlineasm.go +++ b/compiler/inlineasm.go @@ -99,8 +99,8 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) case llvm.IntegerTypeKind: constraints = append(constraints, "r") case llvm.PointerTypeKind: - // Memory references require a type in LLVM 14, probably as a - // preparation for opaque pointers. + // Memory references require a type starting with LLVM 14, + // probably as a preparation for opaque pointers. err = b.makeError(instr.Pos(), "support for pointer operands was dropped in TinyGo 0.23") return s default: diff --git a/compiler/interface.go b/compiler/interface.go index dff5b9241..6a45dd787 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -414,10 +414,10 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { }, typeFields...) if hasMethodSet { typeFields = append([]llvm.Value{ - llvm.ConstBitCast(c.getTypeMethodSet(typ), c.i8ptrType), + c.getTypeMethodSet(typ), }, typeFields...) } - alignment := c.targetData.TypeAllocSize(c.i8ptrType) + alignment := c.targetData.TypeAllocSize(c.dataPtrType) if alignment < 4 { alignment = 4 } @@ -628,7 +628,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value { // Construct global value. globalValue := c.ctx.ConstStruct([]llvm.Value{ llvm.ConstInt(c.uintptrType, uint64(ms.Len()), false), - llvm.ConstArray(c.i8ptrType, signatures), + llvm.ConstArray(c.dataPtrType, signatures), c.ctx.ConstStruct(wrappers, false), }, false) global = llvm.AddGlobal(c.mod, globalValue.Type(), globalName) @@ -779,7 +779,7 @@ func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) ll fnName := s + ".$typeassert" llvmFn := c.mod.NamedFunction(fnName) if llvmFn.IsNil() { - llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.i8ptrType}, false) + llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.dataPtrType}, false) llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType) c.addStandardDeclaredAttributes(llvmFn) methods := c.getMethodsString(assertedType.Underlying().(*types.Interface)) @@ -802,7 +802,7 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value { paramTuple = append(paramTuple, sig.Params().At(i)) } paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer])) - llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false)) + llvmFnType := c.getLLVMFunctionType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false)) llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType) c.addStandardDeclaredAttributes(llvmFn) llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method))) @@ -842,7 +842,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType } // create wrapper function - paramTypes := append([]llvm.Type{c.i8ptrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...) + paramTypes := append([]llvm.Type{c.dataPtrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...) wrapFnType := llvm.FunctionType(llvmFnType.ReturnType(), paramTypes, false) wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType) c.addStandardAttributes(wrapper) diff --git a/compiler/interrupt.go b/compiler/interrupt.go index 1fb4c22b4..6ba031819 100644 --- a/compiler/interrupt.go +++ b/compiler/interrupt.go @@ -36,7 +36,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro // Fall back to a generic error. return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant") } - _, funcRawPtr, funcContext := b.decodeFuncValue(funcValue, nil) + funcRawPtr, funcContext := b.decodeFuncValue(funcValue) funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType) // Create a new global of type runtime/interrupt.handle. Globals of this diff --git a/compiler/intrinsics.go b/compiler/intrinsics.go index af3a57de1..c1d05348b 100644 --- a/compiler/intrinsics.go +++ b/compiler/intrinsics.go @@ -7,7 +7,6 @@ import ( "strconv" "strings" - "github.com/tinygo-org/tinygo/compiler/llvmutil" "tinygo.org/x/go-llvm" ) @@ -48,12 +47,9 @@ func (b *builder) defineIntrinsicFunction() { func (b *builder) createMemoryCopyImpl() { b.createFunctionStart(true) fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth()) - if llvmutil.Major() < 15 { // compatibility with LLVM 14 - fnName = "llvm." + b.fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth()) - } llvmFn := b.mod.NamedFunction(fnName) if llvmFn.IsNil() { - fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false) + fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType, b.dataPtrType, b.uintptrType, b.ctx.Int1Type()}, false) llvmFn = llvm.AddFunction(b.mod, fnName, fnType) } var params []llvm.Value @@ -84,12 +80,9 @@ func (b *builder) createMemoryZeroImpl() { // Return the llvm.memset.p0.i8 function declaration. func (c *compilerContext) getMemsetFunc() llvm.Value { fnName := "llvm.memset.p0.i" + strconv.Itoa(c.uintptrType.IntTypeWidth()) - if llvmutil.Major() < 15 { // compatibility with LLVM 14 - fnName = "llvm.memset.p0i8.i" + strconv.Itoa(c.uintptrType.IntTypeWidth()) - } llvmFn := c.mod.NamedFunction(fnName) if llvmFn.IsNil() { - fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false) + fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.dataPtrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false) llvmFn = llvm.AddFunction(c.mod, fnName, fnType) } return llvmFn @@ -111,7 +104,7 @@ func (b *builder) createKeepAliveImpl() { // // It should be portable to basically everything as the "r" register type // exists basically everywhere. - asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType}, false) + asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType}, false) asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false) b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "") diff --git a/compiler/llvm.go b/compiler/llvm.go index 0d33ab564..968d28b88 100644 --- a/compiler/llvm.go +++ b/compiler/llvm.go @@ -20,7 +20,7 @@ import ( // // This is useful for creating temporary allocas for intrinsics. Don't forget to // end the lifetime using emitLifetimeEnd after you're done with it. -func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitcast, size llvm.Value) { +func (b *builder) createTemporaryAlloca(t llvm.Type, name string) (alloca, size llvm.Value) { return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name) } @@ -63,47 +63,45 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value { // Allocate memory for the packed data. size := b.targetData.TypeAllocSize(packedType) if size == 0 { - return llvm.ConstPointerNull(b.i8ptrType) + return llvm.ConstPointerNull(b.dataPtrType) } else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind { - return b.CreateBitCast(values[0], b.i8ptrType, "pack.ptr") - } else if size <= b.targetData.TypeAllocSize(b.i8ptrType) { + return values[0] + } else if size <= b.targetData.TypeAllocSize(b.dataPtrType) { // Packed data fits in a pointer, so store it directly inside the // pointer. if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind { // Try to keep this cast in SSA form. - return b.CreateIntToPtr(values[0], b.i8ptrType, "pack.int") + return b.CreateIntToPtr(values[0], b.dataPtrType, "pack.int") } // Because packedType is a struct and we have to cast it to a *i8, store // it in a *i8 alloca first and load the *i8 value from there. This is // effectively a bitcast. - packedAlloc, _, _ := b.createTemporaryAlloca(b.i8ptrType, "") + packedAlloc, _ := b.createTemporaryAlloca(b.dataPtrType, "") - if size < b.targetData.TypeAllocSize(b.i8ptrType) { + if size < b.targetData.TypeAllocSize(b.dataPtrType) { // The alloca is bigger than the value that will be stored in it. // To avoid having some bits undefined, zero the alloca first. // Hopefully this will get optimized away. - b.CreateStore(llvm.ConstNull(b.i8ptrType), packedAlloc) + b.CreateStore(llvm.ConstNull(b.dataPtrType), packedAlloc) } // Store all values in the alloca. - packedAllocCast := b.CreateBitCast(packedAlloc, llvm.PointerType(packedType, 0), "") for i, value := range values { indices := []llvm.Value{ llvm.ConstInt(b.ctx.Int32Type(), 0, false), llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false), } - gep := b.CreateInBoundsGEP(packedType, packedAllocCast, indices, "") + gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "") b.CreateStore(value, gep) } // Load value (the *i8) from the alloca. - result := b.CreateLoad(b.i8ptrType, packedAlloc, "") + result := b.CreateLoad(b.dataPtrType, packedAlloc, "") // End the lifetime of the alloca, to help the optimizer. - packedPtr := b.CreateBitCast(packedAlloc, b.i8ptrType, "") packedSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(packedAlloc.Type()), false) - b.emitLifetimeEnd(packedPtr, packedSize) + b.emitLifetimeEnd(packedAlloc, packedSize) return result } else { @@ -124,21 +122,20 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value { global.SetGlobalConstant(true) global.SetUnnamedAddr(true) global.SetLinkage(llvm.InternalLinkage) - return llvm.ConstBitCast(global, b.i8ptrType) + return global } // Packed data is bigger than a pointer, so allocate it on the heap. sizeValue := llvm.ConstInt(b.uintptrType, size, false) alloc := b.mod.NamedFunction("runtime.alloc") - packedHeapAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{ + packedAlloc := b.CreateCall(alloc.GlobalValueType(), alloc, []llvm.Value{ sizeValue, - llvm.ConstNull(b.i8ptrType), - llvm.Undef(b.i8ptrType), // unused context parameter + llvm.ConstNull(b.dataPtrType), + llvm.Undef(b.dataPtrType), // unused context parameter }, "") if b.NeedsStackObjects { - b.trackPointer(packedHeapAlloc) + b.trackPointer(packedAlloc) } - packedAlloc := b.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "") // Store all values in the heap pointer. for i, value := range values { @@ -151,7 +148,7 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value { } // Return the original heap allocation pointer, which already is an *i8. - return packedHeapAlloc + return packedAlloc } } @@ -160,28 +157,27 @@ func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []ll packedType := b.ctx.StructType(valueTypes, false) // Get a correctly-typed pointer to the packed data. - var packedAlloc, packedRawAlloc llvm.Value + var packedAlloc llvm.Value + needsLifetimeEnd := false size := b.targetData.TypeAllocSize(packedType) if size == 0 { // No data to unpack. } else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind { // A single pointer is always stored directly. - return []llvm.Value{b.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")} - } else if size <= b.targetData.TypeAllocSize(b.i8ptrType) { + return []llvm.Value{ptr} + } else if size <= b.targetData.TypeAllocSize(b.dataPtrType) { // Packed data stored directly in pointer. if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind { // Keep this cast in SSA form. return []llvm.Value{b.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")} } // Fallback: load it using an alloca. - packedRawAlloc, _, _ = b.createTemporaryAlloca(llvm.PointerType(b.i8ptrType, 0), "unpack.raw.alloc") - packedRawValue := b.CreateBitCast(ptr, llvm.PointerType(b.i8ptrType, 0), "unpack.raw.value") - b.CreateStore(packedRawValue, packedRawAlloc) - packedAlloc = b.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc") + packedAlloc, _ = b.createTemporaryAlloca(b.dataPtrType, "unpack.raw.alloc") + b.CreateStore(ptr, packedAlloc) + needsLifetimeEnd = true } else { - // Packed data stored on the heap. Bitcast the passed-in pointer to the - // correct pointer type. - packedAlloc = b.CreateBitCast(ptr, llvm.PointerType(packedType, 0), "unpack.raw.ptr") + // Packed data stored on the heap. + packedAlloc = ptr } // Load each value from the packed data. values := make([]llvm.Value, len(valueTypes)) @@ -198,10 +194,9 @@ func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []ll gep := b.CreateInBoundsGEP(packedType, packedAlloc, indices, "") values[i] = b.CreateLoad(valueType, gep, "") } - if !packedRawAlloc.IsNil() { - allocPtr := b.CreateBitCast(packedRawAlloc, b.i8ptrType, "") + if needsLifetimeEnd { allocSize := llvm.ConstInt(b.ctx.Int64Type(), b.targetData.TypeAllocSize(b.uintptrType), false) - b.emitLifetimeEnd(allocPtr, allocSize) + b.emitLifetimeEnd(packedAlloc, allocSize) } return values } @@ -253,12 +248,12 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va // Do a few checks to see whether we need to generate any object layout // information at all. objectSizeBytes := c.targetData.TypeAllocSize(t) - pointerSize := c.targetData.TypeAllocSize(c.i8ptrType) - pointerAlignment := c.targetData.PrefTypeAlignment(c.i8ptrType) + pointerSize := c.targetData.TypeAllocSize(c.dataPtrType) + pointerAlignment := c.targetData.PrefTypeAlignment(c.dataPtrType) if objectSizeBytes < pointerSize { // Too small to contain a pointer. layout := (uint64(1) << 1) | 1 - return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType) + return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType) } bitmap := c.getPointerBitmap(t, pos) if bitmap.BitLen() == 0 { @@ -266,13 +261,13 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va // TODO: this can be done in many other cases, e.g. when allocating an // array (like [4][]byte, which repeats a slice 4 times). layout := (uint64(1) << 1) | 1 - return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType) + return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType) } if objectSizeBytes%uint64(pointerAlignment) != 0 { // This shouldn't happen except for packed structs, which aren't // currently used. c.addError(pos, "internal error: unexpected object size for object with pointer field") - return llvm.ConstNull(c.i8ptrType) + return llvm.ConstNull(c.dataPtrType) } objectSizeWords := objectSizeBytes / uint64(pointerAlignment) @@ -297,7 +292,7 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va // The runtime knows that if the least significant bit of the pointer is // set, the pointer contains the value itself. layout := bitmap.Uint64()<<(sizeFieldBits+1) | (objectSizeWords << 1) | 1 - return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType) + return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType) } // Unfortunately, the object layout is too big to fit in a pointer-sized @@ -308,7 +303,7 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap) global := c.mod.NamedGlobal(globalName) if !global.IsNil() { - return llvm.ConstBitCast(global, c.i8ptrType) + return global } // Create the global initializer. @@ -359,13 +354,13 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va global.AddMetadata(0, diglobal) } - return llvm.ConstBitCast(global, c.i8ptrType) + return global } // getPointerBitmap scans the given LLVM type for pointers and sets bits in a // bigint at the word offset that contains a pointer. This scan is recursive. func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.Int { - alignment := c.targetData.PrefTypeAlignment(c.i8ptrType) + alignment := c.targetData.PrefTypeAlignment(c.dataPtrType) switch typ.TypeKind() { case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind: return big.NewInt(0) @@ -378,7 +373,7 @@ func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.In // of type uintptr, but before the LowerFuncValues pass it actually // contains a pointer (ptrtoint) to a global. This trips up the // interp package. Therefore, make the id field a pointer for now. - typ = c.ctx.StructType([]llvm.Type{c.i8ptrType, c.i8ptrType}, false) + typ = c.ctx.StructType([]llvm.Type{c.dataPtrType, c.dataPtrType}, false) } for i, subtyp := range typ.StructElementTypes() { subptrs := c.getPointerBitmap(subtyp, pos) @@ -458,7 +453,7 @@ func (c *compilerContext) isThumb() bool { func (b *builder) readStackPointer() llvm.Value { stacksave := b.mod.NamedFunction("llvm.stacksave") if stacksave.IsNil() { - fnType := llvm.FunctionType(b.i8ptrType, nil, false) + fnType := llvm.FunctionType(b.dataPtrType, nil, false) stacksave = llvm.AddFunction(b.mod, "llvm.stacksave", fnType) } return b.CreateCall(stacksave.GlobalValueType(), stacksave, nil, "") diff --git a/compiler/llvmutil/llvm.go b/compiler/llvmutil/llvm.go index a79ecf9c4..c07cd0560 100644 --- a/compiler/llvmutil/llvm.go +++ b/compiler/llvmutil/llvm.go @@ -8,22 +8,9 @@ package llvmutil import ( - "strconv" - "strings" - "tinygo.org/x/go-llvm" ) -// Major returns the LLVM major version. -func Major() int { - llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0]) - if err != nil { - // sanity check, should be unreachable - panic("could not parse LLVM version: " + err.Error()) - } - return llvmMajor -} - // CreateEntryBlockAlloca creates a new alloca in the entry block, even though // the IR builder is located elsewhere. It assumes that the insert point is // at the end of the current block. @@ -46,16 +33,14 @@ func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm // // This is useful for creating temporary allocas for intrinsics. Don't forget to // end the lifetime using emitLifetimeEnd after you're done with it. -func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) { +func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, size llvm.Value) { ctx := t.Context() targetData := llvm.NewTargetData(mod.DataLayout()) defer targetData.Dispose() - i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) alloca = CreateEntryBlockAlloca(builder, t, name) - bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast") size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false) fnType, fn := getLifetimeStartFunc(mod) - builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "") + builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "") return } @@ -64,21 +49,19 @@ func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, ctx := mod.Context() targetData := llvm.NewTargetData(mod.DataLayout()) defer targetData.Dispose() - i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) alloca := CreateEntryBlockAlloca(builder, t, name) builder.SetInsertPointBefore(inst) - bitcast := builder.CreateBitCast(alloca, i8ptrType, name+".bitcast") size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false) fnType, fn := getLifetimeStartFunc(mod) - builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "") + builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "") if next := llvm.NextInstruction(inst); !next.IsNil() { builder.SetInsertPointBefore(next) } else { builder.SetInsertPointAtEnd(inst.InstructionParent()) } fnType, fn = getLifetimeEndFunc(mod) - builder.CreateCall(fnType, fn, []llvm.Value{size, bitcast}, "") + builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "") return alloca } @@ -94,13 +77,10 @@ func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value // first if it doesn't exist yet. func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) { fnName := "llvm.lifetime.start.p0" - if Major() < 15 { // compatibility with LLVM 14 - fnName = "llvm.lifetime.start.p0i8" - } fn := mod.NamedFunction(fnName) ctx := mod.Context() - i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) - fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false) + ptrType := llvm.PointerType(ctx.Int8Type(), 0) + fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false) if fn.IsNil() { fn = llvm.AddFunction(mod, fnName, fnType) } @@ -111,13 +91,10 @@ func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) { // first if it doesn't exist yet. func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) { fnName := "llvm.lifetime.end.p0" - if Major() < 15 { - fnName = "llvm.lifetime.end.p0i8" - } fn := mod.NamedFunction(fnName) ctx := mod.Context() - i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) - fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), i8ptrType}, false) + ptrType := llvm.PointerType(ctx.Int8Type(), 0) + fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false) if fn.IsNil() { fn = llvm.AddFunction(mod, fnName, fnType) } @@ -213,13 +190,15 @@ func AppendToGlobal(mod llvm.Module, globalName string, values ...llvm.Value) { } // Add the new values. - i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) + ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) for _, value := range values { - usedValues = append(usedValues, llvm.ConstPointerCast(value, i8ptrType)) + // Note: the bitcast is necessary to cast AVR function pointers to + // address space 0 pointer types. + usedValues = append(usedValues, llvm.ConstPointerCast(value, ptrType)) } // Create a new array (with the old and new values). - usedInitializer := llvm.ConstArray(i8ptrType, usedValues) + usedInitializer := llvm.ConstArray(ptrType, usedValues) used := llvm.AddGlobal(mod, usedInitializer.Type(), globalName) used.SetInitializer(usedInitializer) used.SetLinkage(llvm.AppendingLinkage) diff --git a/compiler/map.go b/compiler/map.go index 21f0ee4a6..1c124c2b2 100644 --- a/compiler/map.go +++ b/compiler/map.go @@ -65,7 +65,7 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val // Allocate the memory for the resulting type. Do not zero this memory: it // will be zeroed by the hashmap get implementation if the key is not // present in the map. - mapValueAlloca, mapValuePtr, mapValueAllocaSize := b.createTemporaryAlloca(llvmValueType, "hashmap.value") + mapValueAlloca, mapValueAllocaSize := b.createTemporaryAlloca(llvmValueType, "hashmap.value") // We need the map size (with type uintptr) to pass to the hashmap*Get // functions. This is necessary because those *Get functions are valid on @@ -82,19 +82,19 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val keyType = keyType.Underlying() if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { // key is a string - params := []llvm.Value{m, key, mapValuePtr, mapValueSize} + params := []llvm.Value{m, key, mapValueAlloca, mapValueSize} commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "") } else if hashmapIsBinaryKey(keyType) { // key can be compared with runtime.memequal // Store the key in an alloca, in the entry block to avoid dynamic stack // growth. - mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") + mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") b.CreateStore(key, mapKeyAlloca) b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca) // Fetch the value from the hashmap. - params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize} + params := []llvm.Value{m, mapKeyAlloca, mapValueAlloca, mapValueSize} commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "") - b.emitLifetimeEnd(mapKeyPtr, mapKeySize) + b.emitLifetimeEnd(mapKeyAlloca, mapKeySize) } else { // Not trivially comparable using memcmp. Make it an interface instead. itfKey := key @@ -102,14 +102,14 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val // Not already an interface, so convert it to an interface now. itfKey = b.createMakeInterface(key, origKeyType, pos) } - params := []llvm.Value{m, itfKey, mapValuePtr, mapValueSize} + params := []llvm.Value{m, itfKey, mapValueAlloca, mapValueSize} commaOkValue = b.createRuntimeCall("hashmapInterfaceGet", params, "") } // Load the resulting value from the hashmap. The value is set to the zero // value if the key doesn't exist in the hashmap. mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "") - b.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize) + b.emitLifetimeEnd(mapValueAlloca, mapValueAllocaSize) if commaOk { tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{llvmValueType, b.ctx.Int1Type()}, false)) @@ -124,22 +124,22 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val // createMapUpdate updates a map key to a given value, by creating an // appropriate runtime call. func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) { - valueAlloca, valuePtr, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value") + valueAlloca, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value") b.CreateStore(value, valueAlloca) origKeyType := keyType keyType = keyType.Underlying() if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { // key is a string - params := []llvm.Value{m, key, valuePtr} + params := []llvm.Value{m, key, valueAlloca} b.createRuntimeCall("hashmapStringSet", params, "") } else if hashmapIsBinaryKey(keyType) { // key can be compared with runtime.memequal - keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") + keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") b.CreateStore(key, keyAlloca) b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca) - params := []llvm.Value{m, keyPtr, valuePtr} + params := []llvm.Value{m, keyAlloca, valueAlloca} b.createRuntimeCall("hashmapBinarySet", params, "") - b.emitLifetimeEnd(keyPtr, keySize) + b.emitLifetimeEnd(keyAlloca, keySize) } else { // Key is not trivially comparable, so compare it as an interface instead. itfKey := key @@ -147,10 +147,10 @@ func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, // Not already an interface, so convert it to an interface first. itfKey = b.createMakeInterface(key, origKeyType, pos) } - params := []llvm.Value{m, itfKey, valuePtr} + params := []llvm.Value{m, itfKey, valueAlloca} b.createRuntimeCall("hashmapInterfaceSet", params, "") } - b.emitLifetimeEnd(valuePtr, valueSize) + b.emitLifetimeEnd(valueAlloca, valueSize) } // createMapDelete deletes a key from a map by calling the appropriate runtime @@ -164,12 +164,12 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok b.createRuntimeCall("hashmapStringDelete", params, "") return nil } else if hashmapIsBinaryKey(keyType) { - keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") + keyAlloca, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key") b.CreateStore(key, keyAlloca) b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca) - params := []llvm.Value{m, keyPtr} + params := []llvm.Value{m, keyAlloca} b.createRuntimeCall("hashmapBinaryDelete", params, "") - b.emitLifetimeEnd(keyPtr, keySize) + b.emitLifetimeEnd(keyAlloca, keySize) return nil } else { // Key is not trivially comparable, so compare it as an interface @@ -225,9 +225,9 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv } // Extract the key and value from the map. - mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key") - mapValueAlloca, mapValuePtr, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value") - ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next") + mapKeyAlloca, mapKeySize := b.createTemporaryAlloca(llvmStoredKeyType, "range.key") + mapValueAlloca, mapValueSize := b.createTemporaryAlloca(llvmValueType, "range.value") + ok := b.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyAlloca, mapValueAlloca}, "range.next") mapKey := b.CreateLoad(llvmStoredKeyType, mapKeyAlloca, "") mapValue := b.CreateLoad(llvmValueType, mapValueAlloca, "") @@ -238,8 +238,8 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv } // End the lifetimes of the allocas, because we're done with them. - b.emitLifetimeEnd(mapKeyPtr, mapKeySize) - b.emitLifetimeEnd(mapValuePtr, mapValueSize) + b.emitLifetimeEnd(mapKeyAlloca, mapKeySize) + b.emitLifetimeEnd(mapValueAlloca, mapValueSize) // Construct the *ssa.Next return value: {ok, mapKey, mapValue} tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.Int1Type(), llvmKeyType, llvmValueType}, false)) @@ -333,14 +333,7 @@ func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error { if fieldEndOffset != nextOffset { n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false) llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false) - gepPtr := elemPtr - if gepPtr.Type() != b.i8ptrType { - gepPtr = b.CreateBitCast(gepPtr, b.i8ptrType, "") // LLVM 14 - } - paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), gepPtr, []llvm.Value{llvmStoreSize}, "") - if paddingStart.Type() != b.i8ptrType { - paddingStart = b.CreateBitCast(paddingStart, b.i8ptrType, "") // LLVM 14 - } + paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), elemPtr, []llvm.Value{llvmStoreSize}, "") b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "") } } diff --git a/compiler/symbol.go b/compiler/symbol.go index 60e882d52..bf5ac5f1b 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -96,7 +96,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value) // Add an extra parameter as the function context. This context is used in // closures and bound methods, but should be optimized away when not used. if !info.exported { - paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", elemSize: 0}) + paramInfos = append(paramInfos, paramInfo{llvmType: c.dataPtrType, name: "context", elemSize: 0}) } var paramTypes []llvm.Type @@ -145,20 +145,18 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value) for _, attrName := range []string{"noalias", "nonnull"} { llvmFn.AddAttributeAtIndex(0, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(attrName), 0)) } - if llvmutil.Major() >= 15 { // allockind etc are not available in LLVM 14 - // Add attributes to signal to LLVM that this is an allocator - // function. This enables a number of optimizations. - llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allockind"), allocKindAlloc|allocKindZeroed)) - llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("alloc-family", "runtime.alloc")) - // Use a special value to indicate the first parameter: - // > allocsize has two integer arguments, but because they're both 32 bits, we can - // > pack them into one 64-bit value, at the cost of making said value - // > nonsensical. - // > - // > In order to do this, we need to reserve one value of the second (optional) - // > allocsize argument to signify "not present." - llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allocsize"), 0x0000_0000_ffff_ffff)) - } + // Add attributes to signal to LLVM that this is an allocator function. + // This enables a number of optimizations. + llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allockind"), allocKindAlloc|allocKindZeroed)) + llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("alloc-family", "runtime.alloc")) + // Use a special value to indicate the first parameter: + // > allocsize has two integer arguments, but because they're both 32 bits, we can + // > pack them into one 64-bit value, at the cost of making said value + // > nonsensical. + // > + // > In order to do this, we need to reserve one value of the second (optional) + // > allocsize argument to signify "not present." + llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allocsize"), 0x0000_0000_ffff_ffff)) case "runtime.sliceAppend": // Appending a slice will only read the to-be-appended slice, it won't // be modified. @@ -445,15 +443,10 @@ func (c *compilerContext) addStandardDefinedAttributes(llvmFn llvm.Value) { llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0)) if strings.Split(c.Triple, "-")[0] == "x86_64" { // Required by the ABI. - if llvmutil.Major() < 15 { - // Needed for LLVM 14 support. - llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 0)) - } else { - // The uwtable has two possible values: sync (1) or async (2). We - // use sync because we currently don't use async unwind tables. - // For details, see: https://llvm.org/docs/LangRef.html#function-attributes - llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 1)) - } + // The uwtable has two possible values: sync (1) or async (2). We use + // sync because we currently don't use async unwind tables. + // For details, see: https://llvm.org/docs/LangRef.html#function-attributes + llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 1)) } } diff --git a/compiler/syscall.go b/compiler/syscall.go index db1ffd700..2623ea94c 100644 --- a/compiler/syscall.go +++ b/compiler/syscall.go @@ -183,7 +183,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) { } llvmType := llvm.FunctionType(b.uintptrType, paramTypes, false) fn := b.getValue(call.Args[0], getPos(call)) - fnPtr := b.CreateIntToPtr(fn, llvm.PointerType(llvmType, 0), "") + fnPtr := b.CreateIntToPtr(fn, b.dataPtrType, "") // Prepare some functions that will be called later. setLastError := b.mod.NamedFunction("SetLastError") diff --git a/interp/interp.go b/interp/interp.go index 7833dfe93..63a664920 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -21,7 +21,7 @@ type runner struct { targetData llvm.TargetData builder llvm.Builder pointerSize uint32 // cached pointer size from the TargetData - i8ptrType llvm.Type // often used type so created in advance + dataPtrType llvm.Type // often used type so created in advance uintptrType llvm.Type // equivalent to uintptr in Go maxAlign int // maximum alignment of an object, alignment of runtime.alloc() result debug bool // log debug messages @@ -46,9 +46,9 @@ func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner { timeout: timeout, } r.pointerSize = uint32(r.targetData.PointerSize()) - r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0) + r.dataPtrType = llvm.PointerType(mod.Context().Int8Type(), 0) r.uintptrType = mod.Context().IntType(r.targetData.PointerSize() * 8) - r.maxAlign = r.targetData.PrefTypeAlignment(r.i8ptrType) // assume pointers are maximally aligned (this is not always the case) + r.maxAlign = r.targetData.PrefTypeAlignment(r.dataPtrType) // assume pointers are maximally aligned (this is not always the case) return &r } @@ -126,7 +126,7 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error { mem.revert() // Create a call to the package initializer (which was // previously deleted). - i8undef := llvm.Undef(r.i8ptrType) + i8undef := llvm.Undef(r.dataPtrType) r.builder.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{i8undef}, "") // Make sure that any globals touched by the package // initializer, won't be accessed by later package initializers. @@ -174,8 +174,7 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error { newGlobal.SetLinkage(obj.llvmGlobal.Linkage()) newGlobal.SetAlignment(obj.llvmGlobal.Alignment()) // TODO: copy debug info, unnamed_addr, ... - bitcast := llvm.ConstBitCast(newGlobal, obj.llvmGlobal.Type()) - obj.llvmGlobal.ReplaceAllUsesWith(bitcast) + obj.llvmGlobal.ReplaceAllUsesWith(newGlobal) name := obj.llvmGlobal.Name() obj.llvmGlobal.EraseFromParentAsGlobal() newGlobal.SetName(name) diff --git a/interp/interpreter.go b/interp/interpreter.go index 3150aca93..24f5e9e85 100644 --- a/interp/interpreter.go +++ b/interp/interpreter.go @@ -1046,15 +1046,3 @@ func intPredicateString(predicate llvm.IntPredicate) string { return "cmp?" } } - -// Strip some pointer casts. This is probably unnecessary once support for -// LLVM 14 (non-opaque pointers) is dropped. -func stripPointerCasts(value llvm.Value) llvm.Value { - if !value.IsAConstantExpr().IsNil() { - switch value.Opcode() { - case llvm.GetElementPtr, llvm.BitCast: - return stripPointerCasts(value.Operand(0)) - } - } - return value -} diff --git a/interp/memory.go b/interp/memory.go index 2065bcdf5..356ffa512 100644 --- a/interp/memory.go +++ b/interp/memory.go @@ -658,20 +658,11 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val if v.offset() != 0 { // If there is an offset, make sure to use a GEP to index into the // pointer. - // Cast to an i8* first (if needed) for easy indexing. - if llvmValue.Type() != mem.r.i8ptrType { - llvmValue = llvm.ConstBitCast(llvmValue, mem.r.i8ptrType) - } llvmValue = llvm.ConstInBoundsGEP(mem.r.mod.Context().Int8Type(), llvmValue, []llvm.Value{ llvm.ConstInt(mem.r.mod.Context().Int32Type(), uint64(v.offset()), false), }) } - // If a particular LLVM pointer type is requested, cast to it. - if !llvmType.IsNil() && llvmType != llvmValue.Type() { - llvmValue = llvm.ConstBitCast(llvmValue, llvmType) - } - return llvmValue, nil } @@ -872,7 +863,7 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, if err != nil { panic(err) } - if checks && mem.r.targetData.TypeAllocSize(llvmType) != mem.r.targetData.TypeAllocSize(mem.r.i8ptrType) { + if checks && mem.r.targetData.TypeAllocSize(llvmType) != mem.r.targetData.TypeAllocSize(mem.r.dataPtrType) { // Probably trying to serialize a pointer to a byte array, // perhaps as a result of rawLLVMValue() in a previous interp // run. @@ -954,8 +945,6 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, // Because go-llvm doesn't have addrspacecast at the moment, // do it indirectly with a ptrtoint/inttoptr pair. llvmValue = llvm.ConstIntToPtr(llvm.ConstPtrToInt(llvmValue, mem.r.uintptrType), llvmType) - } else { - llvmValue = llvm.ConstBitCast(llvmValue, llvmType) } } return llvmValue, nil @@ -1256,7 +1245,7 @@ func (r *runner) getValue(llvmValue llvm.Value) value { // For details on this format, see src/runtime/gc_precise.go. func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) { pointerSize := layoutValue.len(r) - if checks && uint64(pointerSize) != r.targetData.TypeAllocSize(r.i8ptrType) { + if checks && uint64(pointerSize) != r.targetData.TypeAllocSize(r.dataPtrType) { panic("inconsistent pointer size") } @@ -1331,12 +1320,12 @@ func (r *runner) getLLVMTypeFromLayout(layoutValue value) llvm.Type { // Create the LLVM type. pointerSize := layoutValue.len(r) - pointerAlignment := r.targetData.PrefTypeAlignment(r.i8ptrType) + pointerAlignment := r.targetData.PrefTypeAlignment(r.dataPtrType) var fields []llvm.Type for i := 0; i < int(objectSizeWords); { if bitmap.Bit(i) != 0 { // Pointer field. - fields = append(fields, r.i8ptrType) + fields = append(fields, r.dataPtrType) i += int(pointerSize / uint32(pointerAlignment)) } else { // Byte/word field. diff --git a/targets/avrtiny.S b/targets/avrtiny.S index 75f0c604f..6b1a195cf 100644 --- a/targets/avrtiny.S +++ b/targets/avrtiny.S @@ -1,4 +1,3 @@ -; TODO: remove these in LLVM 15 #define __tmp_reg__ r16 #define __zero_reg__ r17 diff --git a/transform/allocs.go b/transform/allocs.go index 5be7df2d3..908d72e7b 100644 --- a/transform/allocs.go +++ b/transform/allocs.go @@ -37,7 +37,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, logger func(tok targetData := llvm.NewTargetData(mod.DataLayout()) defer targetData.Dispose() - i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) + ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) builder := mod.Context().NewBuilder() defer builder.Dispose() @@ -110,7 +110,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, logger func(tok } else { alignment = 8 } - if pointerAlignment := targetData.ABITypeAlignment(i8ptrType); pointerAlignment < alignment { + if pointerAlignment := targetData.ABITypeAlignment(ptrType); pointerAlignment < alignment { // Use min(alignment, alignof(void*)) as the alignment. alignment = pointerAlignment } @@ -120,7 +120,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, logger func(tok fn := bitcast.InstructionParent().Parent() builder.SetInsertPointBefore(fn.EntryBasicBlock().FirstInstruction()) allocaType := llvm.ArrayType(mod.Context().Int8Type(), int(size)) - alloca := builder.CreateAlloca(allocaType, "stackalloc.alloca") + alloca := builder.CreateAlloca(allocaType, "stackalloc") alloca.SetAlignment(alignment) // Zero the allocation inside the block where the value was originally allocated. @@ -130,8 +130,7 @@ func OptimizeAllocs(mod llvm.Module, printAllocs *regexp.Regexp, logger func(tok store.SetAlignment(alignment) // Replace heap alloc bitcast with stack alloc bitcast. - stackalloc := builder.CreateBitCast(alloca, bitcast.Type(), "stackalloc") - bitcast.ReplaceAllUsesWith(stackalloc) + bitcast.ReplaceAllUsesWith(alloca) if heapalloc != bitcast { bitcast.EraseFromParentAsInstruction() } diff --git a/transform/gc.go b/transform/gc.go index a9b5ba749..9d1d8f3fb 100644 --- a/transform/gc.go +++ b/transform/gc.go @@ -225,8 +225,7 @@ func MakeGCStackSlots(mod llvm.Module) bool { llvm.ConstInt(ctx.Int32Type(), 0, false), }, "") builder.CreateStore(parent, gep) - stackObjectCast := builder.CreateBitCast(stackObject, stackChainStartType, "") - builder.CreateStore(stackObjectCast, stackChainStart) + builder.CreateStore(stackObject, stackChainStart) // Do a store to the stack object after each new pointer that is created. pointerStores := make(map[llvm.Value]struct{}) diff --git a/transform/interface-lowering.go b/transform/interface-lowering.go index 11d81e4f6..7f0b6fdc5 100644 --- a/transform/interface-lowering.go +++ b/transform/interface-lowering.go @@ -92,7 +92,7 @@ type lowerInterfacesPass struct { ctx llvm.Context uintptrType llvm.Type targetData llvm.TargetData - i8ptrType llvm.Type + ptrType llvm.Type types map[string]*typeInfo signatures map[string]*signatureInfo interfaces map[string]*interfaceInfo @@ -113,7 +113,7 @@ func LowerInterfaces(mod llvm.Module, config *compileopts.Config) error { ctx: ctx, targetData: targetData, uintptrType: mod.Context().IntType(targetData.PointerSize() * 8), - i8ptrType: llvm.PointerType(ctx.Int8Type(), 0), + ptrType: llvm.PointerType(ctx.Int8Type(), 0), types: make(map[string]*typeInfo), signatures: make(map[string]*signatureInfo), interfaces: make(map[string]*interfaceInfo), @@ -356,11 +356,9 @@ func (p *lowerInterfacesPass) run() error { } // Fallback. if hasUses(t.typecode) { - bitcast := llvm.ConstBitCast(newGlobal, p.i8ptrType) - negativeOffset := -int64(p.targetData.TypeAllocSize(p.i8ptrType)) - gep := p.builder.CreateInBoundsGEP(p.ctx.Int8Type(), bitcast, []llvm.Value{llvm.ConstInt(p.ctx.Int32Type(), uint64(negativeOffset), true)}, "") - bitcast2 := llvm.ConstBitCast(gep, t.typecode.Type()) - t.typecode.ReplaceAllUsesWith(bitcast2) + negativeOffset := -int64(p.targetData.TypeAllocSize(p.ptrType)) + gep := p.builder.CreateInBoundsGEP(p.ctx.Int8Type(), newGlobal, []llvm.Value{llvm.ConstInt(p.ctx.Int32Type(), uint64(negativeOffset), true)}, "") + t.typecode.ReplaceAllUsesWith(gep) } t.typecode.EraseFromParentAsGlobal() newGlobal.SetName(typecodeName) @@ -514,7 +512,7 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte params[i] = fn.Param(i + 1) } params = append(params, - llvm.Undef(p.i8ptrType), + llvm.Undef(p.ptrType), ) // Start chain in the entry block. @@ -554,27 +552,12 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte p.builder.SetInsertPointAtEnd(bb) receiver := fn.FirstParam() - if receiver.Type() != function.FirstParam().Type() { - // When the receiver is a pointer, it is not wrapped. This means the - // i8* has to be cast to the correct pointer type of the target - // function. - receiver = p.builder.CreateBitCast(receiver, function.FirstParam().Type(), "") - } - // Check whether the called function has the same signature as would be - // expected from the parameters. This can happen in rare cases when - // named struct types are renamed after merging multiple LLVM modules. paramTypes := []llvm.Type{receiver.Type()} for _, param := range params { paramTypes = append(paramTypes, param.Type()) } - calledFunctionType := function.Type() functionType := llvm.FunctionType(returnType, paramTypes, false) - sig := llvm.PointerType(functionType, calledFunctionType.PointerAddressSpace()) - if sig != function.Type() { - function = p.builder.CreateBitCast(function, sig, "") - } - retval := p.builder.CreateCall(functionType, function, append([]llvm.Value{receiver}, params...), "") if retval.Type().TypeKind() == llvm.VoidTypeKind { p.builder.CreateRetVoid() @@ -596,7 +579,7 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte // method on a nil interface. nilPanic := p.mod.NamedFunction("runtime.nilPanic") p.builder.CreateCall(nilPanic.GlobalValueType(), nilPanic, []llvm.Value{ - llvm.Undef(p.i8ptrType), + llvm.Undef(p.ptrType), }, "") p.builder.CreateUnreachable() } diff --git a/transform/testdata/allocs.out.ll b/transform/testdata/allocs.out.ll index d1b07e6c4..b3c8bf8f3 100644 --- a/transform/testdata/allocs.out.ll +++ b/transform/testdata/allocs.out.ll @@ -6,18 +6,18 @@ target triple = "armv7m-none-eabi" declare nonnull ptr @runtime.alloc(i32, ptr) define void @testInt() { - %stackalloc.alloca = alloca [4 x i8], align 4 - store [4 x i8] zeroinitializer, ptr %stackalloc.alloca, align 4 - store i32 5, ptr %stackalloc.alloca, align 4 + %stackalloc = alloca [4 x i8], align 4 + store [4 x i8] zeroinitializer, ptr %stackalloc, align 4 + store i32 5, ptr %stackalloc, align 4 ret void } define i16 @testArray() { - %stackalloc.alloca = alloca [6 x i8], align 2 - store [6 x i8] zeroinitializer, ptr %stackalloc.alloca, align 2 - %alloc.1 = getelementptr i16, ptr %stackalloc.alloca, i32 1 + %stackalloc = alloca [6 x i8], align 2 + store [6 x i8] zeroinitializer, ptr %stackalloc, align 2 + %alloc.1 = getelementptr i16, ptr %stackalloc, i32 1 store i16 5, ptr %alloc.1, align 2 - %alloc.2 = getelementptr i16, ptr %stackalloc.alloca, i32 2 + %alloc.2 = getelementptr i16, ptr %stackalloc, i32 2 %val = load i16, ptr %alloc.2, align 2 ret i16 %val } @@ -35,9 +35,9 @@ define void @testEscapingCall2() { } define void @testNonEscapingCall() { - %stackalloc.alloca = alloca [4 x i8], align 4 - store [4 x i8] zeroinitializer, ptr %stackalloc.alloca, align 4 - %val = call ptr @noescapeIntPtr(ptr %stackalloc.alloca) + %stackalloc = alloca [4 x i8], align 4 + store [4 x i8] zeroinitializer, ptr %stackalloc, align 4 + %val = call ptr @noescapeIntPtr(ptr %stackalloc) ret void } @@ -48,12 +48,12 @@ define ptr @testEscapingReturn() { define void @testNonEscapingLoop() { entry: - %stackalloc.alloca = alloca [4 x i8], align 4 + %stackalloc = alloca [4 x i8], align 4 br label %loop loop: ; preds = %loop, %entry - store [4 x i8] zeroinitializer, ptr %stackalloc.alloca, align 4 - %ptr = call ptr @noescapeIntPtr(ptr %stackalloc.alloca) + store [4 x i8] zeroinitializer, ptr %stackalloc, align 4 + %ptr = call ptr @noescapeIntPtr(ptr %stackalloc) %result = icmp eq ptr null, %ptr br i1 %result, label %loop, label %end |