From efdbf0d73567d2d046b048dd4be31ac9e48edee8 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 18 Jul 2017 14:54:34 -0700 Subject: [PATCH] Add Intel encodings for jump and branch instructions. Just implement jump, brz, and brnz as needed for WebAssembly. --- cranelift/filetests/isa/intel/binary32.cton | 22 ++++++++ cranelift/filetests/isa/intel/binary64.cton | 56 +++++++++++++++++++++ cranelift/filetests/wasm/control.cton | 34 +++++++++++++ lib/cretonne/meta/isa/intel/encodings.py | 18 +++++++ lib/cretonne/meta/isa/intel/recipes.py | 46 ++++++++++++++++- lib/cretonne/src/isa/intel/binemit.rs | 14 +++++- 6 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 cranelift/filetests/wasm/control.cton diff --git a/cranelift/filetests/isa/intel/binary32.cton b/cranelift/filetests/isa/intel/binary32.cton index 67f37db11b..7b43ed7aa2 100644 --- a/cranelift/filetests/isa/intel/binary32.cton +++ b/cranelift/filetests/isa/intel/binary32.cton @@ -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 } diff --git a/cranelift/filetests/isa/intel/binary64.cton b/cranelift/filetests/isa/intel/binary64.cton index bed934d5ab..1d88b69d50 100644 --- a/cranelift/filetests/isa/intel/binary64.cton +++ b/cranelift/filetests/isa/intel/binary64.cton @@ -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 } diff --git a/cranelift/filetests/wasm/control.cton b/cranelift/filetests/wasm/control.cton new file mode 100644 index 0000000000..f29c19b5c5 --- /dev/null +++ b/cranelift/filetests/wasm/control.cton @@ -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) +} diff --git a/lib/cretonne/meta/isa/intel/encodings.py b/lib/cretonne/meta/isa/intel/encodings.py index 25421be999..f5e8aabd50 100644 --- a/lib/cretonne/meta/isa/intel/encodings.py +++ b/lib/cretonne/meta/isa/intel/encodings.py @@ -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)) diff --git a/lib/cretonne/meta/isa/intel/recipes.py b/lib/cretonne/meta/isa/intel/recipes.py index 9c48a529d2..f68a8344b3 100644 --- a/lib/cretonne/meta/isa/intel/recipes.py +++ b/lib/cretonne/meta/isa/intel/recipes.py @@ -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); + ''') diff --git a/lib/cretonne/src/isa/intel/binemit.rs b/lib/cretonne/src/isa/intel/binemit.rs index 61c6f512a9..0f3b1b76c6 100644 --- a/lib/cretonne/src/isa/intel/binemit.rs +++ b/lib/cretonne/src/isa/intel/binemit.rs @@ -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(rm: RegUnit, reg: RegUnit, sink: &mut CS) b |= rm; sink.put1(b); } + +/// Emit a single-byte branch displacement to `destination`. +fn disp1(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(destination: Ebb, func: &Function, sink: &mut CS) { + let delta = func.offsets[destination].wrapping_sub(sink.offset() + 4); + sink.put4(delta); +}