Add Intel encodings for jump and branch instructions.

Just implement jump, brz, and brnz as needed for WebAssembly.
This commit is contained in:
Jakob Stoklund Olesen
2017-07-18 14:54:34 -07:00
parent 53d9232d39
commit efdbf0d735
6 changed files with 188 additions and 2 deletions

View File

@@ -262,6 +262,28 @@ ebb0:
; asm: call *%esi
call_indirect sig0, v2() ; bin: ff d6
; asm: testl %ecx, %ecx
; asm: je ebb1
brz v1, ebb1 ; bin: 85 c9 74 0e
; asm: testl %esi, %esi
; asm: je ebb1
brz v2, ebb1 ; bin: 85 f6 74 0a
; asm: testl %ecx, %ecx
; asm: jne ebb1
brnz v1, ebb1 ; bin: 85 c9 75 06
; asm: testl %esi, %esi
; asm: jne ebb1
brnz v2, ebb1 ; bin: 85 f6 75 02
; asm: jmp ebb2
jump ebb2 ; bin: eb 01
; asm: ebb1:
ebb1:
; asm: ret
return ; bin: c3
; asm: ebb2:
ebb2:
jump ebb1 ; bin: eb fd
}

View File

@@ -192,7 +192,35 @@ ebb0:
; asm: tzcntq %rcx, %r10
[-,%r10] v208 = ctz v1 ; bin: f3 4c 0f bc d1
; asm: testq %rcx, %ecx
; asm: je ebb1
brz v1, ebb1 ; bin: 48 85 c9 74 1b
; asm: testq %rsi, %esi
; asm: je ebb1
brz v2, ebb1 ; bin: 48 85 f6 74 16
; asm: testq %r10, %r10d
; asm: je ebb1
brz v3, ebb1 ; bin: 4d 85 d2 74 11
; asm: testq %rcx, %ecx
; asm: jne ebb1
brnz v1, ebb1 ; bin: 48 85 c9 75 0c
; asm: test %rsi, %esi
; asm: jne ebb1
brnz v2, ebb1 ; bin: 48 85 f6 75 07
; asm: testq %r10, %r10d
; asm: jne ebb1
brnz v3, ebb1 ; bin: 4d 85 d2 75 02
; asm: jmp ebb2
jump ebb2 ; bin: eb 01
; asm: ebb1:
ebb1:
return ; bin: c3
; asm: ebb2:
ebb2:
jump ebb1 ; bin: eb fd
}
; Tests for i32 instructions in 64-bit mode.
@@ -384,5 +412,33 @@ ebb0:
; asm: tzcntl %ecx, %r10d
[-,%r10] v208 = ctz v1 ; bin: f3 44 0f bc d1
; asm: testl %ecx, %ecx
; asm: je ebb1
brz v1, ebb1 ; bin: 40 85 c9 74 1b
; asm: testl %esi, %esi
; asm: je ebb1
brz v2, ebb1 ; bin: 40 85 f6 74 16
; asm: testl %r10d, %r10d
; asm: je ebb1
brz v3, ebb1 ; bin: 45 85 d2 74 11
; asm: testl %ecx, %ecx
; asm: jne ebb1
brnz v1, ebb1 ; bin: 40 85 c9 75 0c
; asm: test %esi, %esi
; asm: jne ebb1
brnz v2, ebb1 ; bin: 40 85 f6 75 07
; asm: testl %r10d, %r10d
; asm: jne ebb1
brnz v3, ebb1 ; bin: 45 85 d2 75 02
; asm: jmp ebb2
jump ebb2 ; bin: eb 01
; asm: ebb1:
ebb1:
return ; bin: c3
; asm: ebb2:
ebb2:
jump ebb1 ; bin: eb fd
}

View File

@@ -0,0 +1,34 @@
; Test basic code generation for control flow WebAssembly instructions.
test compile
set is_64bit=0
isa intel haswell
set is_64bit=1
isa intel haswell
function %br_if(i32) -> i32 {
ebb0(v0: i32):
v1 = iconst.i32 1
brz v0, ebb1(v1)
jump ebb2
ebb1(v2: i32):
return v2
ebb2:
jump ebb1(v0)
}
function %br_if_not(i32) -> i32 {
ebb0(v0: i32):
v1 = iconst.i32 1
brnz v0, ebb1(v0)
jump ebb2
ebb1(v2: i32):
return v2
ebb2:
jump ebb1(v0)
}

View File

@@ -156,3 +156,21 @@ I32.enc(base.call, *r.call_id(0xe8))
I32.enc(base.call_indirect.i32, *r.call_r(0xff, rrr=2))
I32.enc(base.x_return, *r.ret(0xc3))
I64.enc(base.x_return, *r.ret(0xc3))
#
# Branches
#
I32.enc(base.jump, *r.jmpb(0xeb))
I32.enc(base.jump, *r.jmpd(0xe9))
I64.enc(base.jump, *r.jmpb(0xeb))
I64.enc(base.jump, *r.jmpd(0xe9))
I32.enc(base.brz.i32, *r.tjccb(0x74))
I64.enc(base.brz.i64, *r.tjccb.rex(0x74, w=1))
I64.enc(base.brz.i32, *r.tjccb.rex(0x74))
I64.enc(base.brz.i32, *r.tjccb(0x74))
I32.enc(base.brnz.i32, *r.tjccb(0x75))
I64.enc(base.brnz.i64, *r.tjccb.rex(0x75, w=1))
I64.enc(base.brnz.i32, *r.tjccb.rex(0x75))
I64.enc(base.brnz.i32, *r.tjccb(0x75))

View File

@@ -6,7 +6,7 @@ from cdsl.isa import EncRecipe
from cdsl.predicates import IsSignedInt, IsEqual
from base.formats import Unary, UnaryImm, Binary, BinaryImm, MultiAry
from base.formats import Call, IndirectCall, Store, Load
from base.formats import RegMove, Ternary
from base.formats import RegMove, Ternary, Jump, Branch
from .registers import GPR, ABCD
try:
@@ -420,3 +420,47 @@ ret = TailRecipe(
emit='''
PUT_OP(bits, BASE_REX, sink);
''')
#
# Branches
#
jmpb = TailRecipe(
'jmpb', Jump, size=1, ins=(), outs=(),
branch_range=(2, 8),
emit='''
PUT_OP(bits, BASE_REX, sink);
disp1(destination, func, sink);
''')
jmpd = TailRecipe(
'jmpd', Jump, size=4, ins=(), outs=(),
branch_range=(5, 32),
emit='''
PUT_OP(bits, BASE_REX, sink);
disp4(destination, func, sink);
''')
# Test-and-branch.
#
# This recipe represents the macro fusion of a test and a conditional branch.
# This serves two purposes:
#
# 1. Guarantee that the test and branch get scheduled next to each other so
# macro fusion is guaranteed to be possible.
# 2. Hide the status flags from Cretonne which doesn't currently model flags.
#
# The encoding bits affect both the test and the branch instruction:
#
# Bits 0-7 are the Jcc opcode.
# Bits 8-15 control the test instruction which always has opcode byte 0x85.
tjccb = TailRecipe(
'tjcc', Branch, size=1 + 2, ins=GPR, outs=(),
branch_range=(2, 8),
emit='''
// test r, r.
PUT_OP((bits & 0xff00) | 0x85, rex2(in_reg0, in_reg0), sink);
modrm_rr(in_reg0, in_reg0, sink);
// Jcc instruction.
sink.put1(bits as u8);
disp1(destination, func, sink);
''')

View File

@@ -1,7 +1,7 @@
//! Emitting binary Intel machine code.
use binemit::{CodeSink, Reloc, bad_encoding};
use ir::{Function, Inst, InstructionData};
use ir::{Function, Inst, Ebb, InstructionData};
use isa::RegUnit;
use regalloc::RegDiversions;
@@ -169,3 +169,15 @@ fn modrm_disp32<CS: CodeSink + ?Sized>(rm: RegUnit, reg: RegUnit, sink: &mut CS)
b |= rm;
sink.put1(b);
}
/// Emit a single-byte branch displacement to `destination`.
fn disp1<CS: CodeSink + ?Sized>(destination: Ebb, func: &Function, sink: &mut CS) {
let delta = func.offsets[destination].wrapping_sub(sink.offset() + 1);
sink.put1(delta as u8);
}
/// Emit a single-byte branch displacement to `destination`.
fn disp4<CS: CodeSink + ?Sized>(destination: Ebb, func: &Function, sink: &mut CS) {
let delta = func.offsets[destination].wrapping_sub(sink.offset() + 4);
sink.put4(delta);
}