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