Skip to content

Commit 69727ed

Browse files
committed
compiler: implement recover() built-in function
1 parent 6767ec3 commit 69727ed

31 files changed

+815
-142
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ TEST_PACKAGES_BASE = \
227227
container/ring \
228228
crypto/des \
229229
crypto/elliptic/internal/fiat \
230+
crypto/hmac \
230231
crypto/internal/subtle \
231232
crypto/md5 \
232233
crypto/rc4 \
@@ -254,10 +255,12 @@ TEST_PACKAGES_BASE = \
254255
os \
255256
path \
256257
reflect \
258+
strconv \
257259
sync \
258260
testing \
259261
testing/iotest \
260262
text/scanner \
263+
text/template/parse \
261264
unicode \
262265
unicode/utf16 \
263266
unicode/utf8 \

compileopts/target.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
319319
// systems so we need separate assembly files.
320320
suffix = "_windows"
321321
}
322-
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+suffix+".S")
322+
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
323323
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
324324
}
325325
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
@@ -137,6 +137,8 @@ type builder struct {
137137
currentBlock *ssa.BasicBlock
138138
phis []phiNode
139139
deferPtr llvm.Value
140+
deferFrame llvm.Value
141+
landingpad llvm.BasicBlock
140142
difunc llvm.Metadata
141143
dilocals map[*types.Var]llvm.Metadata
142144
initInlinedAt llvm.Metadata // fake inlinedAt position
@@ -1038,6 +1040,12 @@ func (b *builder) createFunction() {
10381040
}
10391041
}
10401042

1043+
if b.hasDeferFrame() {
1044+
// Create the landing pad block, where execution continues after a
1045+
// panic.
1046+
b.createLandingPad()
1047+
}
1048+
10411049
// Resolve phi nodes
10421050
for _, phi := range b.phis {
10431051
block := phi.ssa.Block()
@@ -1165,9 +1173,12 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
11651173
b.createMapUpdate(mapType.Key(), m, key, value, instr.Pos())
11661174
case *ssa.Panic:
11671175
value := b.getValue(instr.X)
1168-
b.createRuntimeCall("_panic", []llvm.Value{value}, "")
1176+
b.createRuntimeInvoke("_panic", []llvm.Value{value}, "")
11691177
b.CreateUnreachable()
11701178
case *ssa.Return:
1179+
if b.hasDeferFrame() {
1180+
b.createRuntimeCall("destroyDeferFrame", []llvm.Value{b.deferFrame}, "")
1181+
}
11711182
if len(instr.Results) == 0 {
11721183
b.CreateRetVoid()
11731184
} else if len(instr.Results) == 1 {
@@ -1356,7 +1367,13 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
13561367
cplx := argValues[0]
13571368
return b.CreateExtractValue(cplx, 0, "real"), nil
13581369
case "recover":
1359-
return b.createRuntimeCall("_recover", nil, ""), nil
1370+
useParentFrame := uint64(0)
1371+
if b.hasDeferFrame() {
1372+
// recover() should return the panic value of the parent function,
1373+
// not of the current function.
1374+
useParentFrame = 1
1375+
}
1376+
return b.createRuntimeCall("_recover", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), useParentFrame, false)}, ""), nil
13601377
case "ssa:wrapnilchk":
13611378
// TODO: do an actual nil check?
13621379
return argValues[0], nil
@@ -1443,6 +1460,12 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
14431460
return b.createVolatileLoad(instr)
14441461
case strings.HasPrefix(name, "runtime/volatile.Store"):
14451462
return b.createVolatileStore(instr)
1463+
case name == "runtime.supportsRecover":
1464+
supportsRecover := uint64(0)
1465+
if b.supportsRecover() {
1466+
supportsRecover = 1
1467+
}
1468+
return llvm.ConstInt(b.ctx.Int1Type(), supportsRecover, false), nil
14461469
case strings.HasPrefix(name, "sync/atomic."):
14471470
val, ok := b.createAtomicOp(instr)
14481471
if ok {
@@ -1505,7 +1528,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
15051528
params = append(params, context)
15061529
}
15071530

1508-
return b.createCall(callee, params, ""), nil
1531+
return b.createInvoke(callee, params, ""), nil
15091532
}
15101533

15111534
// 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
@@ -101,6 +101,7 @@ func TestCompiler(t *testing.T) {
101101
}
102102
compilerConfig := &Config{
103103
Triple: config.Triple(),
104+
Features: config.Features(),
104105
GOOS: config.GOOS(),
105106
GOARCH: config.GOARCH(),
106107
CodeModel: config.CodeModel(),

0 commit comments

Comments
 (0)