diff --git a/.circleci/config.yml b/.circleci/config.yml index 1dff3ba719..f7458742f3 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/Makefile b/Makefile index 03c3f17db4..f879138ab0 100644 --- a/Makefile +++ b/Makefile @@ -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 85114d9d72..bcc2dd05bd 100644 --- a/builder/build.go +++ b/builder/build.go @@ -528,13 +528,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() @@ -760,7 +760,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 6517355b6b..5e36c384b1 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 a65a920b73..6ae1fda065 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 861ced9de8..0000000000 --- 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 79aacd2f26..01c3a7e2d1 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 48a9fb2d28..006da5ef8b 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 a110addcf6..f4b76a5135 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 c8c10fe0b6..9969835e84 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 cb3a892a8c..7a1c1e440c 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 92ce31b012..fcb2dd7e2f 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 191b64b91e..e1ff2f58ed 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 3ac42e749c..0af81445c9 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 0fd6f5aaec..9d568a174b 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 8feb5e799c..95abc77ff9 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 72dd68cf3e..5e54b3be60 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 116ad6677e..61db5b5fa2 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 1fb4c22b4c..6ba031819a 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 af3a57de1b..c1d05348b5 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 0d33ab5648..968d28b88b 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 a79ecf9c49..c07cd0560b 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 21f0ee4a67..1c124c2b20 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 60e882d520..bf5ac5f1b7 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 db1ffd7007..2623ea94cf 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 7833dfe932..63a664920d 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 8e5faf642d..36019412d2 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 2065bcdf5f..356ffa512e 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 75f0c604fb..96d81b36ba 100644 --- a/targets/avrtiny.S +++ b/targets/avrtiny.S @@ -1,7 +1,3 @@ -; TODO: remove these in LLVM 15 -#define __tmp_reg__ r16 -#define __zero_reg__ r17 - ; Startup code .section .text.__vector_RESET .global __vector_RESET diff --git a/transform/allocs.go b/transform/allocs.go index 5be7df2d36..908d72e7bc 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 a9b5ba7491..9d1d8f3fbb 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 11d81e4f63..7f0b6fdc57 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 d1b07e6c42..b3c8bf8f39 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