Browse Source

compiler: expand small structs

Use fields of small structs (3 or less fields when flattened
recursively) directly as parameter values.

Advantages:
  * Code size is slightly reduced, both on unix and nrf.
  * AVR can finally deal with struct parameters - at least the small
    ones. examples/test now compiles. A real fix for struct parameters
    should go into upstream LLVM, but this is a nice win.

fixes #20
pull/22/merge
Ayke van Laethem 6 years ago
parent
commit
fdfa810060
No known key found for this signature in database GPG Key ID: E97FF5335DFDFDED
  1. 159
      compiler/calls.go
  2. 179
      compiler/compiler.go

159
compiler/calls.go

@ -0,0 +1,159 @@
package compiler
import (
"go/types"
"github.com/aykevl/llvm/bindings/go/llvm"
"github.com/aykevl/tinygo/ir"
"golang.org/x/tools/go/ssa"
)
// This file implements the calling convention used by Go.
// The calling convention is like the C calling convention (or, whatever LLVM
// makes of it) with the following modifications:
// * Struct parameters are fully expanded to individual fields (recursively),
// when the number of fields (combined) is 3 or less.
// Examples:
// {i8*, i32} -> i8*, i32
// {{i8*, i32}, i16} -> i8*, i32, i16
// {{i64}} -> i64
// {i8*, i32, i8, i8} -> {i8*, i32, i8, i8}
// Note that all native Go data types that don't exist in LLVM (string,
// slice, interface, fat function pointer) can be expanded this way, making
// the work of LLVM optimizers easier.
// * Closures have an extra paramter appended at the end of the argument list,
// which is a pointer to a struct containing free variables.
// * Blocking functions have a coroutine pointer prepended to the argument
// list, see src/runtime/scheduler.go for details.
const MaxFieldsPerParam = 3
func (c *Compiler) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
runtimePkg := c.ir.Program.ImportedPackage("runtime")
member := runtimePkg.Members[fnName]
if member == nil {
panic("trying to call runtime." + fnName)
}
fn := c.ir.GetFunction(member.(*ssa.Function))
return c.createCall(fn, args, name)
}
func (c *Compiler) createCall(fn *ir.Function, args []llvm.Value, name string) llvm.Value {
return c.createIndirectCall(fn.Signature, fn.LLVMFn, args, name)
}
func (c *Compiler) createIndirectCall(sig *types.Signature, fn llvm.Value, args []llvm.Value, name string) llvm.Value {
expanded := make([]llvm.Value, 0, len(args))
for _, arg := range args {
fragments := c.expandFormalParam(arg)
expanded = append(expanded, fragments...)
}
return c.builder.CreateCall(fn, expanded, name)
}
func (c *Compiler) getLLVMParamTypes(t types.Type) ([]llvm.Type, error) {
llvmType, err := c.getLLVMType(t)
if err != nil {
return nil, err
}
return c.expandFormalParamType(llvmType), nil
}
func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type {
switch t.TypeKind() {
case llvm.StructTypeKind:
fields := c.flattenAggregateType(t)
if len(fields) <= MaxFieldsPerParam {
return fields
} else {
// failed to lower
return []llvm.Type{t}
}
default:
// TODO: split small arrays
return []llvm.Type{t}
}
}
// Convert an argument to one that can be passed in a parameter.
func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value {
switch v.Type().TypeKind() {
case llvm.StructTypeKind:
fieldTypes := c.flattenAggregateType(v.Type())
if len(fieldTypes) <= MaxFieldsPerParam {
fields := c.flattenAggregate(v)
if len(fields) != len(fieldTypes) {
panic("type and value param lowering don't match")
}
return fields
} else {
// failed to lower
return []llvm.Value{v}
}
default:
// TODO: split small arrays
return []llvm.Value{v}
}
}
func (c *Compiler) flattenAggregateType(t llvm.Type) []llvm.Type {
switch t.TypeKind() {
case llvm.StructTypeKind:
fields := make([]llvm.Type, 0, len(t.Subtypes()))
for _, subfield := range t.Subtypes() {
subfields := c.flattenAggregateType(subfield)
fields = append(fields, subfields...)
}
return fields
default:
return []llvm.Type{t}
}
}
// Break down a struct into its elementary types for argument passing.
func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value {
switch v.Type().TypeKind() {
case llvm.StructTypeKind:
fields := make([]llvm.Value, 0, len(v.Type().Subtypes()))
for i := range v.Type().Subtypes() {
subfield := c.builder.CreateExtractValue(v, i, "")
subfields := c.flattenAggregate(subfield)
fields = append(fields, subfields...)
}
return fields
default:
return []llvm.Value{v}
}
}
func (c *Compiler) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Value {
param, remaining := c.collapseFormalParamInternal(t, fields)
if len(remaining) != 0 {
panic("failed to expand back all fields")
}
return param
}
// Returns (value, remainingFields).
func (c *Compiler) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
switch t.TypeKind() {
case llvm.StructTypeKind:
if len(c.flattenAggregateType(t)) <= MaxFieldsPerParam {
value, err := getZeroValue(t)
if err != nil {
panic("could not get zero value of struct: " + err.Error())
}
for i, subtyp := range t.Subtypes() {
structField, remaining := c.collapseFormalParamInternal(subtyp, fields)
fields = remaining
value = c.builder.CreateInsertValue(value, structField, i, "")
}
return value, fields
} else {
// this struct was not flattened
return fields[0], fields[1:]
}
default:
return fields[0], fields[1:]
}
}

179
compiler/compiler.go

@ -45,8 +45,6 @@ type Compiler struct {
i8ptrType llvm.Type // for convenience
uintptrType llvm.Type
lenType llvm.Type
allocFunc llvm.Value
freeFunc llvm.Value
coroIdFunc llvm.Value
coroSizeFunc llvm.Value
coroBeginFunc llvm.Value
@ -60,7 +58,6 @@ type Compiler struct {
type Frame struct {
fn *ir.Function
params map[*ssa.Parameter]int // arguments to the function
locals map[ssa.Value]llvm.Value // local variables
blocks map[*ssa.BasicBlock]llvm.BasicBlock
currentBlock *ssa.BasicBlock
@ -112,12 +109,6 @@ func NewCompiler(pkgName, triple string, dumpSSA bool) (*Compiler, error) {
}
c.i8ptrType = llvm.PointerType(llvm.Int8Type(), 0)
allocType := llvm.FunctionType(c.i8ptrType, []llvm.Type{c.uintptrType}, false)
c.allocFunc = llvm.AddFunction(c.mod, "runtime.alloc", allocType)
freeType := llvm.FunctionType(llvm.VoidType(), []llvm.Type{c.i8ptrType}, false)
c.freeFunc = llvm.AddFunction(c.mod, "runtime.free", freeType)
coroIdType := llvm.FunctionType(c.ctx.TokenType(), []llvm.Type{llvm.Int32Type(), c.i8ptrType, c.i8ptrType, c.i8ptrType}, false)
c.coroIdFunc = llvm.AddFunction(c.mod, "llvm.coro.id", coroIdType)
@ -387,7 +378,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
}
// Call real function (of which this is a wrapper).
c.builder.CreateCall(fn.LLVMFn, forwardParams, "")
c.createCall(fn, forwardParams, "")
c.builder.CreateRetVoid()
}
@ -640,7 +631,7 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) {
if recv.StructName() == "runtime._interface" {
recv = c.i8ptrType
}
paramTypes = append(paramTypes, recv)
paramTypes = append(paramTypes, c.expandFormalParamType(recv)...)
}
params := typ.Params()
for i := 0; i < params.Len(); i++ {
@ -648,7 +639,7 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) {
if err != nil {
return llvm.Type{}, err
}
paramTypes = append(paramTypes, subType)
paramTypes = append(paramTypes, c.expandFormalParamType(subType)...)
}
var ptr llvm.Type
if c.ir.SignatureNeedsContext(typ) {
@ -786,7 +777,6 @@ func (c *Compiler) getDIType(typ types.Type) (llvm.Metadata, error) {
func (c *Compiler) parseFuncDecl(f *ir.Function) (*Frame, error) {
frame := &Frame{
fn: f,
params: make(map[*ssa.Parameter]int),
locals: make(map[ssa.Value]llvm.Value),
blocks: make(map[*ssa.BasicBlock]llvm.BasicBlock),
blocking: c.ir.IsBlocking(f),
@ -822,13 +812,12 @@ func (c *Compiler) parseFuncDecl(f *ir.Function) (*Frame, error) {
if frame.blocking {
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
}
for i, param := range f.Params {
paramType, err := c.getLLVMType(param.Type())
for _, param := range f.Params {
paramTypeFragments, err := c.getLLVMParamTypes(param.Type())
if err != nil {
return nil, err
}
paramTypes = append(paramTypes, paramType)
frame.params[param] = i
paramTypes = append(paramTypes, paramTypeFragments...)
}
if c.ir.FunctionNeedsContext(f) {
@ -1197,11 +1186,21 @@ func (c *Compiler) parseFunc(frame *Frame) error {
frame.suspendBlock = c.ctx.AddBasicBlock(frame.fn.LLVMFn, "task.suspend")
}
entryBlock := frame.blocks[frame.fn.Blocks[0]]
c.builder.SetInsertPointAtEnd(entryBlock)
// Load function parameters
llvmParamIndex := 0
for i, param := range frame.fn.Params {
llvmParam := frame.fn.LLVMFn.Param(frame.params[param])
frame.locals[param] = llvmParam
llvmType, err := c.getLLVMType(param.Type())
if err != nil {
return err
}
fields := make([]llvm.Value, 0, 1)
for range c.expandFormalParamType(llvmType) {
fields = append(fields, frame.fn.LLVMFn.Param(llvmParamIndex))
llvmParamIndex++
}
frame.locals[param] = c.collapseFormalParam(llvmType, fields)
// Add debug information to this parameter (if available)
if c.debug && frame.fn.Syntax() != nil {
@ -1228,8 +1227,7 @@ func (c *Compiler) parseFunc(frame *Frame) error {
if !c.ir.FunctionNeedsContext(frame.fn) {
panic("free variables on function without context")
}
c.builder.SetInsertPointAtEnd(entryBlock)
context := frame.fn.LLVMFn.Param(len(frame.fn.Params))
context := frame.fn.LLVMFn.LastParam()
// Determine the context type. It's a struct containing all variables.
freeVarTypes := make([]llvm.Type, 0, len(frame.fn.FreeVars))
@ -1270,8 +1268,6 @@ func (c *Compiler) parseFunc(frame *Frame) error {
}
}
c.builder.SetInsertPointAtEnd(entryBlock)
if frame.fn.Recover != nil {
// Create defer list pointer.
deferType := llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)
@ -1295,15 +1291,15 @@ func (c *Compiler) parseFunc(frame *Frame) error {
} else if c.targetData.TypeAllocSize(size.Type()) < c.targetData.TypeAllocSize(c.uintptrType) {
size = c.builder.CreateZExt(size, c.uintptrType, "task.size.uintptr")
}
data := c.builder.CreateCall(c.allocFunc, []llvm.Value{size}, "task.data")
data := c.createRuntimeCall("alloc", []llvm.Value{size}, "task.data")
frame.taskHandle = c.builder.CreateCall(c.coroBeginFunc, []llvm.Value{id, data}, "task.handle")
// Coroutine cleanup. Free resources associated with this coroutine.
c.builder.SetInsertPointAtEnd(frame.cleanupBlock)
mem := c.builder.CreateCall(c.coroFreeFunc, []llvm.Value{id, frame.taskHandle}, "task.data.free")
c.builder.CreateCall(c.freeFunc, []llvm.Value{mem}, "")
c.createRuntimeCall("free", []llvm.Value{mem}, "")
// re-insert parent coroutine
c.builder.CreateCall(c.mod.NamedFunction("runtime.yieldToScheduler"), []llvm.Value{frame.fn.LLVMFn.FirstParam()}, "")
c.createRuntimeCall("yieldToScheduler", []llvm.Value{frame.fn.LLVMFn.FirstParam()}, "")
c.builder.CreateBr(frame.suspendBlock)
// Coroutine suspend. A call to llvm.coro.suspend() will branch here.
@ -1444,7 +1440,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
if err != nil {
return err
}
c.builder.CreateCall(c.mod.NamedFunction("runtime.yieldToScheduler"), []llvm.Value{handle}, "")
c.createRuntimeCall("yieldToScheduler", []llvm.Value{handle}, "")
return nil
case *ssa.If:
cond, err := c.parseExpr(frame, instr.Cond)
@ -1481,16 +1477,14 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr")
if keyType.Info()&types.IsString != 0 {
params := []llvm.Value{m, key, valuePtr}
fn := c.mod.NamedFunction("runtime.hashmapStringSet")
c.builder.CreateCall(fn, params, "")
c.createRuntimeCall("hashmapStringSet", params, "")
return nil
} else if keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 {
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
c.builder.CreateStore(key, keyAlloca)
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
params := []llvm.Value{m, keyPtr, valuePtr}
fn := c.mod.NamedFunction("runtime.hashmapBinarySet")
c.builder.CreateCall(fn, params, "")
c.createRuntimeCall("hashmapBinarySet", params, "")
return nil
} else {
return errors.New("todo: map update key type: " + keyType.String())
@ -1503,7 +1497,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
if err != nil {
return err
}
c.builder.CreateCall(c.mod.NamedFunction("runtime._panic"), []llvm.Value{value}, "")
c.createRuntimeCall("_panic", []llvm.Value{value}, "")
c.builder.CreateUnreachable()
return nil
case *ssa.Return:
@ -1549,8 +1543,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
}
case *ssa.RunDefers:
deferData := c.builder.CreateLoad(frame.deferPtr, "")
fn := c.mod.NamedFunction("runtime.rundefers")
c.builder.CreateCall(fn, []llvm.Value{deferData}, "")
c.createRuntimeCall("rundefers", []llvm.Value{deferData}, "")
return nil
case *ssa.Store:
llvmAddr, err := c.parseExpr(frame, instr.Addr)
@ -1608,8 +1601,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string)
dstBuf = c.builder.CreateBitCast(dstBuf, c.i8ptrType, "copy.dstPtr")
srcBuf = c.builder.CreateBitCast(srcBuf, c.i8ptrType, "copy.srcPtr")
elemSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(elemType), false)
sliceCopy := c.mod.NamedFunction("runtime.sliceCopy")
return c.builder.CreateCall(sliceCopy, []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
return c.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
case "len":
value, err := c.parseExpr(frame, args[0])
if err != nil {
@ -1637,7 +1629,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string)
case "print", "println":
for i, arg := range args {
if i >= 1 && callName == "println" {
c.builder.CreateCall(c.mod.NamedFunction("runtime.printspace"), nil, "")
c.createRuntimeCall("printspace", nil, "")
}
value, err := c.parseExpr(frame, arg)
if err != nil {
@ -1648,50 +1640,46 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string)
case *types.Basic:
switch typ.Kind() {
case types.String:
c.builder.CreateCall(c.mod.NamedFunction("runtime.printstring"), []llvm.Value{value}, "")
c.createRuntimeCall("printstring", []llvm.Value{value}, "")
case types.Uintptr:
c.builder.CreateCall(c.mod.NamedFunction("runtime.printptr"), []llvm.Value{value}, "")
c.createRuntimeCall("printptr", []llvm.Value{value}, "")
case types.UnsafePointer:
ptrValue := c.builder.CreatePtrToInt(value, c.uintptrType, "")
c.builder.CreateCall(c.mod.NamedFunction("runtime.printptr"), []llvm.Value{ptrValue}, "")
c.createRuntimeCall("printptr", []llvm.Value{ptrValue}, "")
default:
// runtime.print{int,uint}{8,16,32,64}
if typ.Info()&types.IsInteger != 0 {
name := "runtime.print"
name := "print"
if typ.Info()&types.IsUnsigned != 0 {
name += "uint"
} else {
name += "int"
}
name += strconv.FormatUint(c.targetData.TypeAllocSize(value.Type())*8, 10)
fn := c.mod.NamedFunction(name)
if fn.IsNil() {
panic("undefined: " + name)
}
c.builder.CreateCall(fn, []llvm.Value{value}, "")
c.createRuntimeCall(name, []llvm.Value{value}, "")
} else if typ.Kind() == types.Bool {
c.builder.CreateCall(c.mod.NamedFunction("runtime.printbool"), []llvm.Value{value}, "")
c.createRuntimeCall("printbool", []llvm.Value{value}, "")
} else if typ.Kind() == types.Float32 {
c.builder.CreateCall(c.mod.NamedFunction("runtime.printfloat32"), []llvm.Value{value}, "")
c.createRuntimeCall("printfloat32", []llvm.Value{value}, "")
} else if typ.Kind() == types.Float64 {
c.builder.CreateCall(c.mod.NamedFunction("runtime.printfloat64"), []llvm.Value{value}, "")
c.createRuntimeCall("printfloat64", []llvm.Value{value}, "")
} else {
return llvm.Value{}, errors.New("unknown basic arg type: " + typ.String())
}
}
case *types.Interface:
c.builder.CreateCall(c.mod.NamedFunction("runtime.printitf"), []llvm.Value{value}, "")
c.createRuntimeCall("printitf", []llvm.Value{value}, "")
case *types.Map:
c.builder.CreateCall(c.mod.NamedFunction("runtime.printmap"), []llvm.Value{value}, "")
c.createRuntimeCall("printmap", []llvm.Value{value}, "")
case *types.Pointer:
ptrValue := c.builder.CreatePtrToInt(value, c.uintptrType, "")
c.builder.CreateCall(c.mod.NamedFunction("runtime.printptr"), []llvm.Value{ptrValue}, "")
c.createRuntimeCall("printptr", []llvm.Value{ptrValue}, "")
default:
return llvm.Value{}, errors.New("unknown arg type: " + typ.String())
}
}
if callName == "println" {
c.builder.CreateCall(c.mod.NamedFunction("runtime.printnl"), nil, "")
c.createRuntimeCall("printnl", nil, "")
}
return llvm.Value{}, nil // print() or println() returns void
case "ssa:wrapnilchk":
@ -1702,7 +1690,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string)
}
}
func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn, context llvm.Value, blocking bool, parentHandle llvm.Value) (llvm.Value, error) {
func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, fnType *types.Signature, llvmFn, context llvm.Value, blocking bool, parentHandle llvm.Value) (llvm.Value, error) {
var params []llvm.Value
if blocking {
if parentHandle.IsNil() {
@ -1729,7 +1717,7 @@ func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn, con
if frame.blocking && llvmFn.Name() == "time.Sleep" {
// Set task state to TASK_STATE_SLEEP and set the duration.
c.builder.CreateCall(c.mod.NamedFunction("runtime.sleepTask"), []llvm.Value{frame.taskHandle, params[0]}, "")
c.createRuntimeCall("sleepTask", []llvm.Value{frame.taskHandle, params[0]}, "")
// Yield to scheduler.
continuePoint := c.builder.CreateCall(c.coroSuspendFunc, []llvm.Value{
@ -1745,7 +1733,7 @@ func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn, con
return llvm.Value{}, nil
}
result := c.builder.CreateCall(llvmFn, params, "")
result := c.createIndirectCall(fnType, llvmFn, params, "")
if blocking && !parentHandle.IsNil() {
// Calling a blocking function as a regular function call.
// This is done by passing the current coroutine as a parameter to the
@ -1753,10 +1741,10 @@ func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn, con
// (with the TASK_STATE_CALL state). When the subroutine is finished, it
// will reactivate the parent (this frame) in it's destroy function.
c.builder.CreateCall(c.mod.NamedFunction("runtime.yieldToScheduler"), []llvm.Value{result}, "")
c.createRuntimeCall("yieldToScheduler", []llvm.Value{result}, "")
// Set task state to TASK_STATE_CALL.
c.builder.CreateCall(c.mod.NamedFunction("runtime.waitForAsyncCall"), []llvm.Value{frame.taskHandle}, "")
c.createRuntimeCall("waitForAsyncCall", []llvm.Value{frame.taskHandle}, "")
// Yield to the scheduler.
continuePoint := c.builder.CreateCall(c.coroSuspendFunc, []llvm.Value{
@ -1799,7 +1787,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
itf,
llvm.ConstInt(llvm.Int16Type(), uint64(c.ir.MethodNum(instr.Method)), false),
}
fn := c.builder.CreateCall(c.mod.NamedFunction("runtime.interfaceMethod"), values, "invoke.func")
fn := c.createRuntimeCall("interfaceMethod", values, "invoke.func")
fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast")
receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver")
@ -1819,12 +1807,11 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
}
// TODO: blocking methods (needs analysis)
return c.builder.CreateCall(fnCast, args, ""), nil
return c.createIndirectCall(instr.Method.Type().(*types.Signature), fnCast, args, ""), nil
}
// Try to call the function directly for trivially static calls.
fn := instr.StaticCallee()
if fn != nil {
if fn := instr.StaticCallee(); fn != nil {
if fn.Name() == "Asm" && len(instr.Args) == 1 {
// Magic function: insert inline assembly instead of calling it.
if named, ok := instr.Args[0].Type().(*types.Named); ok && named.Obj().Name() == "__asm" {
@ -1858,7 +1845,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
}
}
}
return c.parseFunctionCall(frame, instr.Args, targetFunc.LLVMFn, context, c.ir.IsBlocking(targetFunc), parentHandle)
return c.parseFunctionCall(frame, instr.Args, targetFunc.Signature, targetFunc.LLVMFn, context, c.ir.IsBlocking(targetFunc), parentHandle)
}
// Builtin or function pointer.
@ -1879,7 +1866,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
context = c.builder.CreateExtractValue(value, 0, "")
value = c.builder.CreateExtractValue(value, 1, "")
}
return c.parseFunctionCall(frame, instr.Args, value, context, false, parentHandle)
return c.parseFunctionCall(frame, instr.Args, instr.Value.Type().(*types.Signature), value, context, false, parentHandle)
}
}
@ -1899,8 +1886,7 @@ func (c *Compiler) emitBoundsCheck(frame *Frame, arrayLen, index llvm.Value) {
return
}
}
lookupBoundsCheck := c.mod.NamedFunction("runtime.lookupBoundsCheck")
c.builder.CreateCall(lookupBoundsCheck, []llvm.Value{arrayLen, index}, "")
c.createRuntimeCall("lookupBoundsCheck", []llvm.Value{arrayLen, index}, "")
}
func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
@ -1922,7 +1908,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
if expr.Heap {
// TODO: escape analysis
size := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(typ), false)
buf = c.builder.CreateCall(c.allocFunc, []llvm.Value{size}, expr.Comment)
buf = c.createRuntimeCall("alloc", []llvm.Value{size}, expr.Comment)
buf = c.builder.CreateBitCast(buf, llvm.PointerType(typ, 0), "")
} else {
buf = c.builder.CreateAlloca(typ, expr.Comment)
@ -2126,16 +2112,14 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "hashmap.valueptr")
if keyType.Info()&types.IsString != 0 {
params := []llvm.Value{value, index, mapValuePtr}
fn := c.mod.NamedFunction("runtime.hashmapStringGet")
c.builder.CreateCall(fn, params, "")
c.createRuntimeCall("hashmapStringGet", params, "")
return c.builder.CreateLoad(mapValueAlloca, ""), nil
} else if keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 {
keyAlloca := c.builder.CreateAlloca(index.Type(), "hashmap.key")
c.builder.CreateStore(index, keyAlloca)
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
params := []llvm.Value{value, keyPtr, mapValuePtr}
fn := c.mod.NamedFunction("runtime.hashmapBinaryGet")
c.builder.CreateCall(fn, params, "")
c.createRuntimeCall("hashmapBinaryGet", params, "")
return c.builder.CreateLoad(mapValueAlloca, ""), nil
} else {
return llvm.Value{}, errors.New("todo: map lookup key type: " + keyType.String())
@ -2168,10 +2152,9 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
}
keySize := c.targetData.TypeAllocSize(llvmKeyType)
valueSize := c.targetData.TypeAllocSize(llvmValueType)
hashmapMake := c.mod.NamedFunction("runtime.hashmapMake")
llvmKeySize := llvm.ConstInt(llvm.Int8Type(), keySize, false)
llvmValueSize := llvm.ConstInt(llvm.Int8Type(), valueSize, false)
hashmap := c.builder.CreateCall(hashmapMake, []llvm.Value{llvmKeySize, llvmValueSize}, "")
hashmap := c.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize}, "")
return hashmap, nil
case *ssa.MakeSlice:
sliceLen, err := c.parseExpr(frame, expr.Len)
@ -2191,8 +2174,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
// Bounds checking.
if !frame.fn.IsNoBounds() {
sliceBoundsCheck := c.mod.NamedFunction("runtime.sliceBoundsCheckMake")
c.builder.CreateCall(sliceBoundsCheck, []llvm.Value{sliceLen, sliceCap}, "")
c.createRuntimeCall("sliceBoundsCheckMake", []llvm.Value{sliceLen, sliceCap}, "")
}
// Allocate the backing array.
@ -2203,7 +2185,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
return llvm.Value{}, err
}
sliceSize := c.builder.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
slicePtr := c.builder.CreateCall(c.allocFunc, []llvm.Value{sliceSize}, "makeslice.buf")
slicePtr := c.createRuntimeCall("alloc", []llvm.Value{sliceSize}, "makeslice.buf")
slicePtr = c.builder.CreateBitCast(slicePtr, llvm.PointerType(llvmElemType, 0), "makeslice.array")
if c.targetData.TypeAllocSize(sliceLen.Type()) > c.targetData.TypeAllocSize(c.lenType) {
@ -2232,11 +2214,8 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
return llvm.Value{}, err
}
if expr.IsString {
fn := c.mod.NamedFunction("runtime.stringNext")
return c.builder.CreateCall(fn, []llvm.Value{llvmRangeVal, it}, "range.next"), nil
return c.createRuntimeCall("stringNext", []llvm.Value{llvmRangeVal, it}, "range.next"), nil
} else { // map
fn := c.mod.NamedFunction("runtime.hashmapNext")
llvmKeyType, err := c.getLLVMType(rangeVal.Type().(*types.Map).Key())
if err != nil {
return llvm.Value{}, err
@ -2250,7 +2229,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
mapKeyPtr := c.builder.CreateBitCast(mapKeyAlloca, c.i8ptrType, "range.keyptr")
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "range.value")
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "range.valueptr")
ok := c.builder.CreateCall(fn, []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next")
ok := c.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next")
tuple := llvm.Undef(llvm.StructType([]llvm.Type{llvm.Int1Type(), llvmKeyType, llvmValueType}, false))
tuple = c.builder.CreateInsertValue(tuple, ok, 0, "")
@ -2325,8 +2304,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
// This check is optimized away in most cases.
if !frame.fn.IsNoBounds() {
sliceBoundsCheck := c.mod.NamedFunction("runtime.sliceBoundsCheck")
c.builder.CreateCall(sliceBoundsCheck, []llvm.Value{llvmLen, low, high}, "")
c.createRuntimeCall("sliceBoundsCheck", []llvm.Value{llvmLen, low, high}, "")
}
if c.targetData.TypeAllocSize(sliceLen.Type()) > c.targetData.TypeAllocSize(c.lenType) {
@ -2354,8 +2332,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
}
if !frame.fn.IsNoBounds() {
sliceBoundsCheck := c.mod.NamedFunction("runtime.sliceBoundsCheck")
c.builder.CreateCall(sliceBoundsCheck, []llvm.Value{oldLen, low, high}, "")
c.createRuntimeCall("sliceBoundsCheck", []llvm.Value{oldLen, low, high}, "")
}
if c.targetData.TypeAllocSize(low.Type()) > c.targetData.TypeAllocSize(c.lenType) {
@ -2390,8 +2367,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
}
if !frame.fn.IsNoBounds() {
sliceBoundsCheck := c.mod.NamedFunction("runtime.sliceBoundsCheck")
c.builder.CreateCall(sliceBoundsCheck, []llvm.Value{oldLen, low, high}, "")
c.createRuntimeCall("sliceBoundsCheck", []llvm.Value{oldLen, low, high}, "")
}
newPtr := c.builder.CreateGEP(oldPtr, []llvm.Value{low}, "")
@ -2431,8 +2407,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
// At the same time, the interface value itself is unchanged.
itfTypeNum := c.ir.InterfaceNum(itf)
itfTypeNumValue := llvm.ConstInt(llvm.Int16Type(), uint64(itfTypeNum), false)
fn := c.mod.NamedFunction("runtime.interfaceImplements")
commaOk = c.builder.CreateCall(fn, []llvm.Value{actualTypeNum, itfTypeNumValue}, "")
commaOk = c.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, itfTypeNumValue}, "")
} else {
// Type assert on concrete type.
@ -2447,8 +2422,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
if expr.CommaOk {
return llvm.ConstStruct([]llvm.Value{undef, commaOk}, false), nil
} else {
fn := c.mod.NamedFunction("runtime.interfaceTypeAssert")
c.builder.CreateCall(fn, []llvm.Value{commaOk}, "")
c.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "")
return undef, nil
}
}
@ -2528,8 +2502,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
} else {
// This is kind of dirty as the branch above becomes mostly useless,
// but hopefully this gets optimized away.
fn := c.mod.NamedFunction("runtime.interfaceTypeAssert")
c.builder.CreateCall(fn, []llvm.Value{commaOk}, "")
c.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "")
return phi, nil
}
case *ssa.UnOp:
@ -2682,10 +2655,9 @@ func (c *Compiler) parseBinOp(frame *Frame, binop *ssa.BinOp) (llvm.Value, error
// Operations on strings
switch binop.Op {
case token.ADD:
fn := c.mod.NamedFunction("runtime.stringConcat")
return c.builder.CreateCall(fn, []llvm.Value{x, y}, ""), nil
return c.createRuntimeCall("stringConcat", []llvm.Value{x, y}, ""), nil
case token.EQL, token.NEQ: // ==, !=
result := c.builder.CreateCall(c.mod.NamedFunction("runtime.stringEqual"), []llvm.Value{x, y}, "")
result := c.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "")
if binop.Op == token.NEQ {
result = c.builder.CreateNot(result, "")
}
@ -2717,7 +2689,7 @@ func (c *Compiler) parseBinOp(frame *Frame, binop *ssa.BinOp) (llvm.Value, error
case *types.Interface:
switch binop.Op {
case token.EQL, token.NEQ: // ==, !=
result := c.builder.CreateCall(c.mod.NamedFunction("runtime.interfaceEqual"), []llvm.Value{x, y}, "")
result := c.createRuntimeCall("interfaceEqual", []llvm.Value{x, y}, "")
if binop.Op == token.NEQ {
result = c.builder.CreateNot(result, "")
}
@ -2872,13 +2844,11 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value) (
} else if sizeFrom < 4 {
value = c.builder.CreateSExt(value, llvm.Int32Type(), "")
}
fn := c.mod.NamedFunction("runtime.stringFromUnicode")
return c.builder.CreateCall(fn, []llvm.Value{value}, ""), nil
return c.createRuntimeCall("stringFromUnicode", []llvm.Value{value}, ""), nil
case *types.Slice:
switch typeFrom.Elem().(*types.Basic).Kind() {
case types.Byte:
fn := c.mod.NamedFunction("runtime.stringFromBytes")
return c.builder.CreateCall(fn, []llvm.Value{value}, ""), nil
return c.createRuntimeCall("stringFromBytes", []llvm.Value{value}, ""), nil
default:
return llvm.Value{}, errors.New("todo: convert to string: " + typeFrom.String())
}
@ -2940,8 +2910,7 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value) (
elemType := typeTo.Elem().Underlying().(*types.Basic) // must be byte or rune
switch elemType.Kind() {
case types.Byte:
fn := c.mod.NamedFunction("runtime.stringToBytes")
return c.builder.CreateCall(fn, []llvm.Value{value}, ""), nil
return c.createRuntimeCall("stringToBytes", []llvm.Value{value}, ""), nil
default:
return llvm.Value{}, errors.New("todo: convert from string: " + elemType.String())
}
@ -2988,7 +2957,7 @@ func (c *Compiler) parseMakeClosure(frame *Frame, expr *ssa.MakeClosure) (llvm.V
// Context is bigger than a pointer, so allocate it on the heap.
size := c.targetData.TypeAllocSize(contextType)
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
contextHeapAlloc = c.builder.CreateCall(c.allocFunc, []llvm.Value{sizeValue}, "")
contextHeapAlloc = c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "")
contextAlloc = c.builder.CreateBitCast(contextHeapAlloc, llvm.PointerType(contextType, 0), "")
}
@ -3047,7 +3016,7 @@ func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global str
// Allocate on the heap and put a pointer in the interface.
// TODO: escape analysis.
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
itfValue = c.builder.CreateCall(c.allocFunc, []llvm.Value{sizeValue}, "")
itfValue = c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "")
itfValueCast := c.builder.CreateBitCast(itfValue, llvm.PointerType(val.Type(), 0), "")
c.builder.CreateStore(val, itfValueCast)
}

Loading…
Cancel
Save