
Moving settings to a separate config struct has two benefits: - It decouples the compiler a bit from other packages, most importantly the compileopts package. Decoupling is generally a good thing. - Perhaps more importantly, it precisely specifies which settings are used while compiling and affect the resulting LLVM module. This will be necessary for caching the LLVM module. While it would have been possible to cache without this refactor, it would have been very easy to miss a setting and thus let the compiler work with invalid/stale data.
69 строки
2,5 КиБ
Go
69 строки
2,5 КиБ
Go
package compiler
|
|
|
|
import (
|
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
|
"tinygo.org/x/go-llvm"
|
|
)
|
|
|
|
// This file contains helper functions for LLVM that are not exposed in the Go
|
|
// bindings.
|
|
|
|
// Return a list of values (actually, instructions) where this value is used as
|
|
// an operand.
|
|
func getUses(value llvm.Value) []llvm.Value {
|
|
if value.IsNil() {
|
|
return nil
|
|
}
|
|
var uses []llvm.Value
|
|
use := value.FirstUse()
|
|
for !use.IsNil() {
|
|
uses = append(uses, use.User())
|
|
use = use.NextUse()
|
|
}
|
|
return uses
|
|
}
|
|
|
|
// createTemporaryAlloca creates a new alloca in the entry block and adds
|
|
// lifetime start information in the IR signalling that the alloca won't be used
|
|
// before this point.
|
|
//
|
|
// 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) {
|
|
return llvmutil.CreateTemporaryAlloca(b.Builder, b.mod, t, name)
|
|
}
|
|
|
|
// emitLifetimeEnd signals the end of an (alloca) lifetime by calling the
|
|
// llvm.lifetime.end intrinsic. It is commonly used together with
|
|
// createTemporaryAlloca.
|
|
func (b *builder) emitLifetimeEnd(ptr, size llvm.Value) {
|
|
llvmutil.EmitLifetimeEnd(b.Builder, b.mod, ptr, size)
|
|
}
|
|
|
|
// 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 (b *builder) emitPointerPack(values []llvm.Value) llvm.Value {
|
|
return llvmutil.EmitPointerPack(b.Builder, b.mod, b.NeedsStackObjects, values)
|
|
}
|
|
|
|
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
|
|
func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
|
return llvmutil.EmitPointerUnpack(b.Builder, b.mod, ptr, valueTypes)
|
|
}
|
|
|
|
// makeGlobalArray creates a new LLVM global with the given name and integers as
|
|
// contents, and returns the global.
|
|
// Note that it is left with the default linkage etc., you should set
|
|
// linkage/constant/etc properties yourself.
|
|
func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType llvm.Type) llvm.Value {
|
|
globalType := llvm.ArrayType(elementType, len(buf))
|
|
global := llvm.AddGlobal(c.mod, globalType, name)
|
|
value := llvm.Undef(globalType)
|
|
for i := 0; i < len(buf); i++ {
|
|
ch := uint64(buf[i])
|
|
value = llvm.ConstInsertValue(value, llvm.ConstInt(elementType, ch, false), []uint32{uint32(i)})
|
|
}
|
|
global.SetInitializer(value)
|
|
return global
|
|
}
|