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

Commit

Permalink
exec{,internal/compile}: implement handling of divide by zero in amd6…
Browse files Browse the repository at this point in the history
…4 backend

Fixes #165.
  • Loading branch information
twitchyliquid64 authored and sbinet committed Sep 13, 2019
1 parent 7bc5f1f commit c697622
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 0 deletions.
22 changes: 22 additions & 0 deletions exec/internal/compile/backend_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,29 @@ func (b *AMD64Backend) emitDivide(builder *asm.Builder, ci currentInstruction) {
b.emitSymbolicPopToReg(builder, ci, x86.REG_R9)
b.emitSymbolicPopToReg(builder, ci, x86.REG_AX)

// tst r9, r9
prog := builder.NewProg()
prog.As = x86.ATESTQ
prog.From.Type = obj.TYPE_REG
prog.From.Reg = x86.REG_R9
prog.To.Type = obj.TYPE_REG
prog.To.Reg = x86.REG_R9
builder.AddInstruction(prog)

// jne notZero
jmp := builder.NewProg()
jmp.As = x86.AJNE
jmp.To.Type = obj.TYPE_BRANCH
builder.AddInstruction(jmp)
b.emitExit(builder, CompletionDivideZero|makeExitIndex(ci.idx), false)

// notZero:
prog = builder.NewProg()
prog.As = obj.ANOP // branch target - assembler will optimize out.
jmp.Pcond = prog
builder.AddInstruction(prog)

prog = builder.NewProg()
prog.As = x86.AXORQ
prog.From.Type = obj.TYPE_REG
prog.From.Reg = x86.REG_DX
Expand Down
92 changes: 92 additions & 0 deletions exec/internal/compile/backend_amd64_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,12 @@ func TestDivOps(t *testing.T) {
Args: []uint64{7, 2},
Result: 3,
},
{
Name: "I64-unsigned-divide-3",
Op: ops.I64DivU,
Args: []uint64{200, 20},
Result: 10,
},
{
Name: "I64-unsigned-remainder-1",
Op: ops.I64RemU,
Expand Down Expand Up @@ -1105,6 +1111,92 @@ func TestDivOps(t *testing.T) {
}
}

func TestDivideByZero(t *testing.T) {
if !supportedOS(runtime.GOOS) {
t.SkipNow()
}
testCases := []struct {
Name string
Op byte
Args []uint64
}{
{
Name: "I64-unsigned-divide",
Op: ops.I64DivU,
Args: []uint64{88, 0},
},
{
Name: "I64-signed-divide",
Op: ops.I64DivS,
Args: []uint64{88, 0},
},
{
Name: "I32-unsigned-divide",
Op: ops.I32DivU,
Args: []uint64{88, 0},
},
{
Name: "I32-signed-divide",
Op: ops.I32DivS,
Args: []uint64{88, 0},
},
{
Name: "I64-unsigned-rem",
Op: ops.I64RemU,
Args: []uint64{88, 0},
},
{
Name: "I64-signed-rem",
Op: ops.I64RemS,
Args: []uint64{88, 0},
},
{
Name: "I32-unsigned-rem",
Op: ops.I32RemU,
Args: []uint64{88, 0},
},
{
Name: "I32-signed-rem",
Op: ops.I32RemS,
Args: []uint64{88, 0},
},
}

allocator := &MMapAllocator{}
defer allocator.Close()
b := &AMD64Backend{}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
builder, err := asm.NewBuilder("amd64", 64)
if err != nil {
t.Fatal(err)
}
b.emitPreamble(builder)

for _, arg := range tc.Args {
b.emitPushImmediate(builder, currentInstruction{}, arg)
}
b.emitDivide(builder, currentInstruction{inst: InstructionMetadata{Op: tc.Op}})
b.emitPostamble(builder)
b.lowerAMD64(builder)
out := builder.Assemble()

nativeBlock, err := allocator.AllocateExec(out)
if err != nil {
t.Fatal(err)
}

fakeStack := make([]uint64, 0, 5)
fakeLocals := make([]uint64, 0, 0)
exit := nativeBlock.Invoke(&fakeStack, &fakeLocals, nil, nil)

if exit.CompletionStatus() != CompletionDivideZero {
t.Fatalf("completion status = %v, want CompletionDivideZero", exit.CompletionStatus())
}
})
}
}

func TestComparisonOps64(t *testing.T) {
if !supportedOS(runtime.GOOS) {
t.SkipNow()
Expand Down
1 change: 1 addition & 0 deletions exec/internal/compile/native.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
CompletionBadBounds
CompletionUnreachable
CompletionFatalInternalError
CompletionDivideZero
)

func makeExitIndex(idx int) CompletionStatus {
Expand Down
2 changes: 2 additions & 0 deletions exec/native_compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ func (vm *VM) nativeCodeInvocation(asmIndex uint32) {
panic("fatal error in native execution")
case compile.CompletionBadBounds:
panic("exec: out of bounds memory access")
case compile.CompletionDivideZero:
panic("runtime error: integer divide by zero")
}
vm.ctx.pc = int64(block.resumePC)
}
Expand Down

0 comments on commit c697622

Please sign in to comment.