Skip to content
This repository was archived by the owner on May 11, 2020. It is now read-only.

Commit c697622

Browse files
twitchyliquid64sbinet
authored andcommitted
exec{,internal/compile}: implement handling of divide by zero in amd64 backend
Fixes #165.
1 parent 7bc5f1f commit c697622

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

exec/internal/compile/backend_amd64.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,7 +1064,29 @@ func (b *AMD64Backend) emitDivide(builder *asm.Builder, ci currentInstruction) {
10641064
b.emitSymbolicPopToReg(builder, ci, x86.REG_R9)
10651065
b.emitSymbolicPopToReg(builder, ci, x86.REG_AX)
10661066

1067+
// tst r9, r9
10671068
prog := builder.NewProg()
1069+
prog.As = x86.ATESTQ
1070+
prog.From.Type = obj.TYPE_REG
1071+
prog.From.Reg = x86.REG_R9
1072+
prog.To.Type = obj.TYPE_REG
1073+
prog.To.Reg = x86.REG_R9
1074+
builder.AddInstruction(prog)
1075+
1076+
// jne notZero
1077+
jmp := builder.NewProg()
1078+
jmp.As = x86.AJNE
1079+
jmp.To.Type = obj.TYPE_BRANCH
1080+
builder.AddInstruction(jmp)
1081+
b.emitExit(builder, CompletionDivideZero|makeExitIndex(ci.idx), false)
1082+
1083+
// notZero:
1084+
prog = builder.NewProg()
1085+
prog.As = obj.ANOP // branch target - assembler will optimize out.
1086+
jmp.Pcond = prog
1087+
builder.AddInstruction(prog)
1088+
1089+
prog = builder.NewProg()
10681090
prog.As = x86.AXORQ
10691091
prog.From.Type = obj.TYPE_REG
10701092
prog.From.Reg = x86.REG_DX

exec/internal/compile/backend_amd64_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,12 @@ func TestDivOps(t *testing.T) {
10051005
Args: []uint64{7, 2},
10061006
Result: 3,
10071007
},
1008+
{
1009+
Name: "I64-unsigned-divide-3",
1010+
Op: ops.I64DivU,
1011+
Args: []uint64{200, 20},
1012+
Result: 10,
1013+
},
10081014
{
10091015
Name: "I64-unsigned-remainder-1",
10101016
Op: ops.I64RemU,
@@ -1105,6 +1111,92 @@ func TestDivOps(t *testing.T) {
11051111
}
11061112
}
11071113

1114+
func TestDivideByZero(t *testing.T) {
1115+
if !supportedOS(runtime.GOOS) {
1116+
t.SkipNow()
1117+
}
1118+
testCases := []struct {
1119+
Name string
1120+
Op byte
1121+
Args []uint64
1122+
}{
1123+
{
1124+
Name: "I64-unsigned-divide",
1125+
Op: ops.I64DivU,
1126+
Args: []uint64{88, 0},
1127+
},
1128+
{
1129+
Name: "I64-signed-divide",
1130+
Op: ops.I64DivS,
1131+
Args: []uint64{88, 0},
1132+
},
1133+
{
1134+
Name: "I32-unsigned-divide",
1135+
Op: ops.I32DivU,
1136+
Args: []uint64{88, 0},
1137+
},
1138+
{
1139+
Name: "I32-signed-divide",
1140+
Op: ops.I32DivS,
1141+
Args: []uint64{88, 0},
1142+
},
1143+
{
1144+
Name: "I64-unsigned-rem",
1145+
Op: ops.I64RemU,
1146+
Args: []uint64{88, 0},
1147+
},
1148+
{
1149+
Name: "I64-signed-rem",
1150+
Op: ops.I64RemS,
1151+
Args: []uint64{88, 0},
1152+
},
1153+
{
1154+
Name: "I32-unsigned-rem",
1155+
Op: ops.I32RemU,
1156+
Args: []uint64{88, 0},
1157+
},
1158+
{
1159+
Name: "I32-signed-rem",
1160+
Op: ops.I32RemS,
1161+
Args: []uint64{88, 0},
1162+
},
1163+
}
1164+
1165+
allocator := &MMapAllocator{}
1166+
defer allocator.Close()
1167+
b := &AMD64Backend{}
1168+
for _, tc := range testCases {
1169+
t.Run(tc.Name, func(t *testing.T) {
1170+
builder, err := asm.NewBuilder("amd64", 64)
1171+
if err != nil {
1172+
t.Fatal(err)
1173+
}
1174+
b.emitPreamble(builder)
1175+
1176+
for _, arg := range tc.Args {
1177+
b.emitPushImmediate(builder, currentInstruction{}, arg)
1178+
}
1179+
b.emitDivide(builder, currentInstruction{inst: InstructionMetadata{Op: tc.Op}})
1180+
b.emitPostamble(builder)
1181+
b.lowerAMD64(builder)
1182+
out := builder.Assemble()
1183+
1184+
nativeBlock, err := allocator.AllocateExec(out)
1185+
if err != nil {
1186+
t.Fatal(err)
1187+
}
1188+
1189+
fakeStack := make([]uint64, 0, 5)
1190+
fakeLocals := make([]uint64, 0, 0)
1191+
exit := nativeBlock.Invoke(&fakeStack, &fakeLocals, nil, nil)
1192+
1193+
if exit.CompletionStatus() != CompletionDivideZero {
1194+
t.Fatalf("completion status = %v, want CompletionDivideZero", exit.CompletionStatus())
1195+
}
1196+
})
1197+
}
1198+
}
1199+
11081200
func TestComparisonOps64(t *testing.T) {
11091201
if !supportedOS(runtime.GOOS) {
11101202
t.SkipNow()

exec/internal/compile/native.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const (
3131
CompletionBadBounds
3232
CompletionUnreachable
3333
CompletionFatalInternalError
34+
CompletionDivideZero
3435
)
3536

3637
func makeExitIndex(idx int) CompletionStatus {

exec/native_compile.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ func (vm *VM) nativeCodeInvocation(asmIndex uint32) {
164164
panic("fatal error in native execution")
165165
case compile.CompletionBadBounds:
166166
panic("exec: out of bounds memory access")
167+
case compile.CompletionDivideZero:
168+
panic("runtime error: integer divide by zero")
167169
}
168170
vm.ctx.pc = int64(block.resumePC)
169171
}

0 commit comments

Comments
 (0)