github.com/axw/llgo@v0.0.0-20160805011314-95b5fe4dca20/irgen/indirect.go (about)

     1  //===- indirect.go - IR generation for thunks -----------------------------===//
     2  //
     3  //                     The LLVM Compiler Infrastructure
     4  //
     5  // This file is distributed under the University of Illinois Open Source
     6  // License. See LICENSE.TXT for details.
     7  //
     8  //===----------------------------------------------------------------------===//
     9  //
    10  // This file implements IR generation for thunks required by the "defer" and
    11  // "go" builtins.
    12  //
    13  //===----------------------------------------------------------------------===//
    14  
    15  package irgen
    16  
    17  import (
    18  	"llvm.org/llgo/third_party/gotools/go/ssa"
    19  	"llvm.org/llgo/third_party/gotools/go/types"
    20  	"llvm.org/llvm/bindings/go/llvm"
    21  )
    22  
    23  // createThunk creates a thunk from a
    24  // given function and arguments, suitable for use with
    25  // "defer" and "go".
    26  func (fr *frame) createThunk(call ssa.CallInstruction) (thunk llvm.Value, arg llvm.Value) {
    27  	seenarg := make(map[ssa.Value]bool)
    28  	var args []ssa.Value
    29  	var argtypes []*types.Var
    30  
    31  	packArg := func(arg ssa.Value) {
    32  		switch arg.(type) {
    33  		case *ssa.Builtin, *ssa.Function, *ssa.Const, *ssa.Global:
    34  			// Do nothing: we can generate these in the thunk
    35  		default:
    36  			if !seenarg[arg] {
    37  				seenarg[arg] = true
    38  				args = append(args, arg)
    39  				field := types.NewField(0, nil, "_", arg.Type(), true)
    40  				argtypes = append(argtypes, field)
    41  			}
    42  		}
    43  	}
    44  
    45  	packArg(call.Common().Value)
    46  	for _, arg := range call.Common().Args {
    47  		packArg(arg)
    48  	}
    49  
    50  	var isRecoverCall bool
    51  	i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
    52  	var structllptr llvm.Type
    53  	if len(args) == 0 {
    54  		if builtin, ok := call.Common().Value.(*ssa.Builtin); ok {
    55  			isRecoverCall = builtin.Name() == "recover"
    56  		}
    57  		if isRecoverCall {
    58  			// When creating a thunk for recover(), we must pass fr.canRecover.
    59  			arg = fr.builder.CreateZExt(fr.canRecover, fr.target.IntPtrType(), "")
    60  			arg = fr.builder.CreateIntToPtr(arg, i8ptr, "")
    61  		} else {
    62  			arg = llvm.ConstPointerNull(i8ptr)
    63  		}
    64  	} else {
    65  		structtype := types.NewStruct(argtypes, nil)
    66  		arg = fr.createTypeMalloc(structtype)
    67  		structllptr = arg.Type()
    68  		for i, ssaarg := range args {
    69  			argptr := fr.builder.CreateStructGEP(arg, i, "")
    70  			fr.builder.CreateStore(fr.llvmvalue(ssaarg), argptr)
    71  		}
    72  		arg = fr.builder.CreateBitCast(arg, i8ptr, "")
    73  	}
    74  
    75  	thunkfntype := llvm.FunctionType(llvm.VoidType(), []llvm.Type{i8ptr}, false)
    76  	thunkfn := llvm.AddFunction(fr.module.Module, "", thunkfntype)
    77  	thunkfn.SetLinkage(llvm.InternalLinkage)
    78  	fr.addCommonFunctionAttrs(thunkfn)
    79  
    80  	thunkfr := newFrame(fr.unit, thunkfn)
    81  	defer thunkfr.dispose()
    82  
    83  	prologuebb := llvm.AddBasicBlock(thunkfn, "prologue")
    84  	thunkfr.builder.SetInsertPointAtEnd(prologuebb)
    85  
    86  	if isRecoverCall {
    87  		thunkarg := thunkfn.Param(0)
    88  		thunkarg = thunkfr.builder.CreatePtrToInt(thunkarg, fr.target.IntPtrType(), "")
    89  		thunkfr.canRecover = thunkfr.builder.CreateTrunc(thunkarg, llvm.Int1Type(), "")
    90  	} else if len(args) > 0 {
    91  		thunkarg := thunkfn.Param(0)
    92  		thunkarg = thunkfr.builder.CreateBitCast(thunkarg, structllptr, "")
    93  		for i, ssaarg := range args {
    94  			thunkargptr := thunkfr.builder.CreateStructGEP(thunkarg, i, "")
    95  			thunkarg := thunkfr.builder.CreateLoad(thunkargptr, "")
    96  			thunkfr.env[ssaarg] = newValue(thunkarg, ssaarg.Type())
    97  		}
    98  	}
    99  
   100  	_, isDefer := call.(*ssa.Defer)
   101  
   102  	entrybb := llvm.AddBasicBlock(thunkfn, "entry")
   103  	br := thunkfr.builder.CreateBr(entrybb)
   104  	thunkfr.allocaBuilder.SetInsertPointBefore(br)
   105  
   106  	thunkfr.builder.SetInsertPointAtEnd(entrybb)
   107  	var exitbb llvm.BasicBlock
   108  	if isDefer {
   109  		exitbb = llvm.AddBasicBlock(thunkfn, "exit")
   110  		thunkfr.runtime.setDeferRetaddr.call(thunkfr, llvm.BlockAddress(thunkfn, exitbb))
   111  	}
   112  	if isDefer && isRecoverCall {
   113  		thunkfr.callRecover(true)
   114  	} else {
   115  		thunkfr.callInstruction(call)
   116  	}
   117  	if isDefer {
   118  		thunkfr.builder.CreateBr(exitbb)
   119  		thunkfr.builder.SetInsertPointAtEnd(exitbb)
   120  	}
   121  	thunkfr.builder.CreateRetVoid()
   122  
   123  	thunk = fr.builder.CreateBitCast(thunkfn, i8ptr, "")
   124  	return
   125  }