Skip to content

Commit 7db2c4c

Browse files
committed
compiler: implement recover() built-in function
1 parent f42ca46 commit 7db2c4c

31 files changed

+827
-150
lines changed

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,11 +307,14 @@ TEST_PACKAGES_LINUX := \
307307
archive/zip \
308308
compress/flate \
309309
compress/lzw \
310+
crypto/hmac \
310311
debug/dwarf \
311312
debug/plan9obj \
312313
io/fs \
313314
io/ioutil \
314-
testing/fstest
315+
strconv \
316+
testing/fstest \
317+
text/template/parse
315318

316319
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
317320

compileopts/target.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
302302
// systems so we need separate assembly files.
303303
suffix = "_windows"
304304
}
305-
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+suffix+".S")
305+
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
306306
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
307307
}
308308
if goarch != runtime.GOARCH {

compiler/calls.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,36 @@ const (
3333
paramIsDeferenceableOrNull = 1 << iota
3434
)
3535

36-
// createCall creates a new call to runtime.<fnName> with the given arguments.
37-
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
36+
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
37+
// createRuntimeInvoke instead.
38+
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
3839
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
3940
llvmFn := b.getFunction(fn)
4041
if llvmFn.IsNil() {
4142
panic("trying to call non-existent function: " + fn.RelString(nil))
4243
}
4344
args = append(args, llvm.Undef(b.i8ptrType)) // unused context parameter
45+
if isInvoke {
46+
return b.createInvoke(llvmFn, args, name)
47+
}
4448
return b.createCall(llvmFn, args, name)
4549
}
4650

51+
// createRuntimeCall creates a new call to runtime.<fnName> with the given
52+
// arguments.
53+
func (b *builder) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
54+
return b.createRuntimeCallCommon(fnName, args, name, false)
55+
}
56+
57+
// createRuntimeInvoke creates a new call to runtime.<fnName> with the given
58+
// arguments. If the runtime call panics, control flow is diverted to the
59+
// landing pad block.
60+
// Note that "invoke" here is meant in the LLVM sense (a call that can
61+
// panic/throw), not in the Go sense (an interface method call).
62+
func (b *builder) createRuntimeInvoke(fnName string, args []llvm.Value, name string) llvm.Value {
63+
return b.createRuntimeCallCommon(fnName, args, name, true)
64+
}
65+
4766
// createCall creates a call to the given function with the arguments possibly
4867
// expanded.
4968
func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
@@ -55,6 +74,15 @@ func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm
5574
return b.CreateCall(fn, expanded, name)
5675
}
5776

77+
// createInvoke is like createCall but continues execution at the landing pad if
78+
// the call resulted in a panic.
79+
func (b *builder) createInvoke(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
80+
if b.hasDeferFrame() {
81+
b.createInvokeCheckpoint()
82+
}
83+
return b.createCall(fn, args, name)
84+
}
85+
5886
// Expand an argument type to a list that can be used in a function call
5987
// parameter list.
6088
func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {

compiler/compiler.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ type builder struct {
143143
currentBlock *ssa.BasicBlock
144144
phis []phiNode
145145
deferPtr llvm.Value
146+
deferFrame llvm.Value
147+
landingpad llvm.BasicBlock
146148
difunc llvm.Metadata
147149
dilocals map[*types.Var]llvm.Metadata
148150
initInlinedAt llvm.Metadata // fake inlinedAt position
@@ -1202,6 +1204,12 @@ func (b *builder) createFunction() {
12021204
}
12031205
}
12041206

1207+
if b.hasDeferFrame() {
1208+
// Create the landing pad block, where execution continues after a
1209+
// panic.
1210+
b.createLandingPad()
1211+
}
1212+
12051213
// Resolve phi nodes
12061214
for _, phi := range b.phis {
12071215
block := phi.ssa.Block()
@@ -1329,9 +1337,12 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
13291337
b.createMapUpdate(mapType.Key(), m, key, value, instr.Pos())
13301338
case *ssa.Panic:
13311339
value := b.getValue(instr.X)
1332-
b.createRuntimeCall("_panic", []llvm.Value{value}, "")
1340+
b.createRuntimeInvoke("_panic", []llvm.Value{value}, "")
13331341
b.CreateUnreachable()
13341342
case *ssa.Return:
1343+
if b.hasDeferFrame() {
1344+
b.createRuntimeCall("destroyDeferFrame", []llvm.Value{b.deferFrame}, "")
1345+
}
13351346
if len(instr.Results) == 0 {
13361347
b.CreateRetVoid()
13371348
} else if len(instr.Results) == 1 {
@@ -1520,7 +1531,13 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
15201531
cplx := argValues[0]
15211532
return b.CreateExtractValue(cplx, 0, "real"), nil
15221533
case "recover":
1523-
return b.createRuntimeCall("_recover", nil, ""), nil
1534+
useParentFrame := uint64(0)
1535+
if b.hasDeferFrame() {
1536+
// recover() should return the panic value of the parent function,
1537+
// not of the current function.
1538+
useParentFrame = 1
1539+
}
1540+
return b.createRuntimeCall("_recover", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), useParentFrame, false)}, ""), nil
15241541
case "ssa:wrapnilchk":
15251542
// TODO: do an actual nil check?
15261543
return argValues[0], nil
@@ -1607,6 +1624,12 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
16071624
return b.createVolatileLoad(instr)
16081625
case strings.HasPrefix(name, "runtime/volatile.Store"):
16091626
return b.createVolatileStore(instr)
1627+
case name == "runtime.supportsRecover":
1628+
supportsRecover := uint64(0)
1629+
if b.supportsRecover() {
1630+
supportsRecover = 1
1631+
}
1632+
return llvm.ConstInt(b.ctx.Int1Type(), supportsRecover, false), nil
16101633
case strings.HasPrefix(name, "sync/atomic."):
16111634
val, ok := b.createAtomicOp(instr)
16121635
if ok {
@@ -1677,7 +1700,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
16771700
params = append(params, context)
16781701
}
16791702

1680-
return b.createCall(callee, params, ""), nil
1703+
return b.createInvoke(callee, params, ""), nil
16811704
}
16821705

16831706
// getValue returns the LLVM value of a constant, function value, global, or

compiler/compiler_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ func TestCompiler(t *testing.T) {
9494
}
9595
compilerConfig := &Config{
9696
Triple: config.Triple(),
97+
Features: config.Features(),
9798
GOOS: config.GOOS(),
9899
GOARCH: config.GOARCH(),
99100
CodeModel: config.CodeModel(),

0 commit comments

Comments
 (0)