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