aboutsummaryrefslogtreecommitdiffhomepage
path: root/compiler/llvmutil/wordpack.go
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-11-13 18:08:24 +0100
committerAyke <[email protected]>2019-11-15 23:37:17 +0100
commit36d119811534752904dad8a369e5587d5750442f (patch)
tree149ac39e2e99b1ac3abd8d1a77673169ba74a94f /compiler/llvmutil/wordpack.go
parent009b27350e58019169fe5ee59ec216acec2ae2f5 (diff)
downloadtinygo-36d119811534752904dad8a369e5587d5750442f.tar.gz
tinygo-36d119811534752904dad8a369e5587d5750442f.zip
compiler: refactor alloca/lifetime/wordpack code into separate package
This code is required by transformation passes which are being moved into a separate package, but is too complicated to simply copy. Therefore, I decided to move them into a new package.
Diffstat (limited to 'compiler/llvmutil/wordpack.go')
-rw-r--r--compiler/llvmutil/wordpack.go133
1 files changed, 133 insertions, 0 deletions
diff --git a/compiler/llvmutil/wordpack.go b/compiler/llvmutil/wordpack.go
new file mode 100644
index 000000000..1cbada7ee
--- /dev/null
+++ b/compiler/llvmutil/wordpack.go
@@ -0,0 +1,133 @@
+package llvmutil
+
+// This file contains utility functions to pack and unpack sets of values. It
+// can take in a list of values and tries to store it efficiently in the pointer
+// itself if possible and legal.
+
+import (
+ "github.com/tinygo-org/tinygo/compileopts"
+ "tinygo.org/x/go-llvm"
+)
+
+// EmitPointerPack packs the list of values into a single pointer value using
+// bitcasts, or else allocates a value on the heap if it cannot be packed in the
+// pointer value directly. It returns the pointer with the packed data.
+func EmitPointerPack(builder llvm.Builder, mod llvm.Module, config *compileopts.Config, values []llvm.Value) llvm.Value {
+ ctx := mod.Context()
+ targetData := llvm.NewTargetData(mod.DataLayout())
+ i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
+ uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
+
+ valueTypes := make([]llvm.Type, len(values))
+ for i, value := range values {
+ valueTypes[i] = value.Type()
+ }
+ packedType := ctx.StructType(valueTypes, false)
+
+ // Allocate memory for the packed data.
+ var packedAlloc, packedHeapAlloc llvm.Value
+ size := targetData.TypeAllocSize(packedType)
+ if size == 0 {
+ return llvm.ConstPointerNull(i8ptrType)
+ } else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
+ return builder.CreateBitCast(values[0], i8ptrType, "pack.ptr")
+ } else if size <= targetData.TypeAllocSize(i8ptrType) {
+ // 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 builder.CreateIntToPtr(values[0], i8ptrType, "pack.int")
+ }
+ // Because packedType is a struct and we have to cast it to a *i8, store
+ // it in an alloca first for bitcasting (store+bitcast+load).
+ packedAlloc, _, _ = CreateTemporaryAlloca(builder, mod, packedType, "")
+ } else {
+ // Packed data is bigger than a pointer, so allocate it on the heap.
+ sizeValue := llvm.ConstInt(uintptrType, size, false)
+ alloc := mod.NamedFunction("runtime.alloc")
+ packedHeapAlloc = builder.CreateCall(alloc, []llvm.Value{sizeValue}, "")
+ if config.NeedsStackObjects() {
+ trackPointer := mod.NamedFunction("runtime.trackPointer")
+ builder.CreateCall(trackPointer, []llvm.Value{packedHeapAlloc}, "")
+ }
+ packedAlloc = builder.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "")
+ }
+ // Store all values in the alloca or heap pointer.
+ for i, value := range values {
+ indices := []llvm.Value{
+ llvm.ConstInt(ctx.Int32Type(), 0, false),
+ llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
+ }
+ gep := builder.CreateInBoundsGEP(packedAlloc, indices, "")
+ builder.CreateStore(value, gep)
+ }
+
+ if packedHeapAlloc.IsNil() {
+ // Load value (as *i8) from the alloca.
+ packedAlloc = builder.CreateBitCast(packedAlloc, llvm.PointerType(i8ptrType, 0), "")
+ result := builder.CreateLoad(packedAlloc, "")
+ packedPtr := builder.CreateBitCast(packedAlloc, i8ptrType, "")
+ packedSize := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(packedAlloc.Type()), false)
+ EmitLifetimeEnd(builder, mod, packedPtr, packedSize)
+ return result
+ } else {
+ // Get the original heap allocation pointer, which already is an *i8.
+ return packedHeapAlloc
+ }
+}
+
+// EmitPointerUnpack extracts a list of values packed using EmitPointerPack.
+func EmitPointerUnpack(builder llvm.Builder, mod llvm.Module, ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
+ ctx := mod.Context()
+ targetData := llvm.NewTargetData(mod.DataLayout())
+ i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
+ uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
+
+ packedType := ctx.StructType(valueTypes, false)
+
+ // Get a correctly-typed pointer to the packed data.
+ var packedAlloc, packedRawAlloc llvm.Value
+ size := 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{builder.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
+ } else if size <= targetData.TypeAllocSize(i8ptrType) {
+ // Packed data stored directly in pointer.
+ if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
+ // Keep this cast in SSA form.
+ return []llvm.Value{builder.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
+ }
+ // Fallback: load it using an alloca.
+ packedRawAlloc, _, _ = CreateTemporaryAlloca(builder, mod, llvm.PointerType(i8ptrType, 0), "unpack.raw.alloc")
+ packedRawValue := builder.CreateBitCast(ptr, llvm.PointerType(i8ptrType, 0), "unpack.raw.value")
+ builder.CreateStore(packedRawValue, packedRawAlloc)
+ packedAlloc = builder.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
+ } else {
+ // Packed data stored on the heap. Bitcast the passed-in pointer to the
+ // correct pointer type.
+ packedAlloc = builder.CreateBitCast(ptr, llvm.PointerType(packedType, 0), "unpack.raw.ptr")
+ }
+ // Load each value from the packed data.
+ values := make([]llvm.Value, len(valueTypes))
+ for i, valueType := range valueTypes {
+ if targetData.TypeAllocSize(valueType) == 0 {
+ // This value has length zero, so there's nothing to load.
+ values[i] = llvm.ConstNull(valueType)
+ continue
+ }
+ indices := []llvm.Value{
+ llvm.ConstInt(ctx.Int32Type(), 0, false),
+ llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
+ }
+ gep := builder.CreateInBoundsGEP(packedAlloc, indices, "")
+ values[i] = builder.CreateLoad(gep, "")
+ }
+ if !packedRawAlloc.IsNil() {
+ allocPtr := builder.CreateBitCast(packedRawAlloc, i8ptrType, "")
+ allocSize := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(uintptrType), false)
+ EmitLifetimeEnd(builder, mod, allocPtr, allocSize)
+ }
+ return values
+}