Optimize immediates and compare and branch sequences (#286)
* Add a pre-opt optimization to change constants into immediates. This converts 'iadd' + 'iconst' into 'iadd_imm', and so on. * Optimize away redundant `bint` instructions. Cretonne has a concept of "Testable" values, which can be either boolean or integer. When the an instruction needing a "Testable" value receives the result of a `bint`, converting boolean to integer, eliminate the `bint`, as it's redundant. * Postopt: Optimize using CPU flags. This introduces a post-legalization optimization pass which converts compare+branch sequences to use flags values on CPUs which support it. * Define a form of x86's `urm` that doesn't clobber FLAGS. movzbl/movsbl/etc. don't clobber FLAGS; define a form of the `urm` recipe that represents this. * Implement a DCE pass. This pass deletes instructions with no side effects and no results that are used. * Clarify ambiguity about "32-bit" and "64-bit" in comments. * Add x86 encodings for icmp_imm. * Add a testcase for postopt CPU flags optimization. This covers the basic functionality of transforming compare+branch sequences to use CPU flags. * Pattern-match irsub_imm in preopt.
This commit is contained in:
@@ -334,6 +334,14 @@ Test the LICM pass.
|
||||
The LICM pass is run on each function, and then results are run
|
||||
through filecheck.
|
||||
|
||||
`test dce`
|
||||
-----------------
|
||||
|
||||
Test the DCE pass.
|
||||
|
||||
The DCE pass is run on each function, and then results are run
|
||||
through filecheck.
|
||||
|
||||
`test preopt`
|
||||
-----------------
|
||||
|
||||
@@ -342,6 +350,14 @@ Test the preopt pass.
|
||||
The preopt pass is run on each function, and then results are run
|
||||
through filecheck.
|
||||
|
||||
`test postopt`
|
||||
-----------------
|
||||
|
||||
Test the postopt pass.
|
||||
|
||||
The postopt pass is run on each function, and then results are run
|
||||
through filecheck.
|
||||
|
||||
`test compile`
|
||||
--------------
|
||||
|
||||
|
||||
46
cranelift/filetests/dce/basic.cton
Normal file
46
cranelift/filetests/dce/basic.cton
Normal file
@@ -0,0 +1,46 @@
|
||||
test dce
|
||||
|
||||
function %simple() -> i32 {
|
||||
ebb0:
|
||||
v2 = iconst.i32 2
|
||||
v3 = iconst.i32 3
|
||||
return v3
|
||||
}
|
||||
; sameln: function %simple
|
||||
; nextln: ebb0:
|
||||
; nextln: v3 = iconst.i32 3
|
||||
; nextln: return v3
|
||||
; nextln: }
|
||||
|
||||
function %some_branching(i32, i32) -> i32 {
|
||||
ebb0(v0: i32, v1: i32):
|
||||
v3 = iconst.i32 70
|
||||
v4 = iconst.i32 71
|
||||
v5 = iconst.i32 72
|
||||
v8 = iconst.i32 73
|
||||
brz v0, ebb1
|
||||
jump ebb2(v8)
|
||||
|
||||
ebb1:
|
||||
v2 = iadd v0, v3
|
||||
return v0
|
||||
|
||||
ebb2(v9: i32):
|
||||
v6 = iadd v1, v4
|
||||
v7 = iadd v6, v9
|
||||
return v7
|
||||
}
|
||||
; sameln: function %some_branching
|
||||
; nextln: ebb0(v0: i32, v1: i32):
|
||||
; nextln: v4 = iconst.i32 71
|
||||
; nextln: v8 = iconst.i32 73
|
||||
; nextln: brz v0, ebb1
|
||||
; nextln: jump ebb2(v8)
|
||||
; nextln:
|
||||
; nextln: ebb1:
|
||||
; nextln: return v0
|
||||
; nextln:
|
||||
; nextln: ebb2(v9: i32):
|
||||
; nextln: v6 = iadd.i32 v1, v4
|
||||
; nextln: v7 = iadd v6, v9
|
||||
; nextln: return v7
|
||||
@@ -1,4 +1,4 @@
|
||||
; binary emission of 32-bit code.
|
||||
; binary emission of x86-32 code.
|
||||
test binemit
|
||||
set is_compressed
|
||||
isa intel haswell
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; binary emission of 64-bit code.
|
||||
; binary emission of x86-64 code.
|
||||
test binemit
|
||||
set is_64bit
|
||||
set is_compressed
|
||||
@@ -458,6 +458,14 @@ ebb0:
|
||||
; asm: setbe %dl
|
||||
[-,%rdx] v319 = icmp ule v2, v3 ; bin: 4c 39 d6 0f 96 c2
|
||||
|
||||
; asm: cmpq $37, %rcx
|
||||
; asm: setl %bl
|
||||
[-,%rbx] v320 = icmp_imm slt v1, 37 ; bin: 48 83 f9 25 0f 9c c3
|
||||
|
||||
; asm: cmpq $100000, %rcx
|
||||
; asm: setl %bl
|
||||
[-,%rbx] v321 = icmp_imm slt v1, 100000 ; bin: 48 81 f9 000186a0 0f 9c c3
|
||||
|
||||
; Bool-to-int conversions.
|
||||
|
||||
; asm: movzbq %bl, %rcx
|
||||
@@ -1031,6 +1039,14 @@ ebb0:
|
||||
; asm: setbe %dl
|
||||
[-,%rdx] v319 = icmp ule v2, v3 ; bin: 44 39 d6 0f 96 c2
|
||||
|
||||
; asm: cmpl $37, %ecx
|
||||
; asm: setl %bl
|
||||
[-,%rbx] v320 = icmp_imm slt v1, 37 ; bin: 83 f9 25 0f 9c c3
|
||||
|
||||
; asm: cmpq $100000, %ecx
|
||||
; asm: setl %bl
|
||||
[-,%rbx] v321 = icmp_imm slt v1, 100000 ; bin: 81 f9 000186a0 0f 9c c3
|
||||
|
||||
; Bool-to-int conversions.
|
||||
|
||||
; asm: movzbl %bl, %ecx
|
||||
|
||||
100
cranelift/filetests/postopt/basic.cton
Normal file
100
cranelift/filetests/postopt/basic.cton
Normal file
@@ -0,0 +1,100 @@
|
||||
test postopt
|
||||
isa intel
|
||||
|
||||
; Test that compare+branch sequences are folded effectively on x86.
|
||||
|
||||
function %br_icmp(i32, i32) -> i32 {
|
||||
ebb0(v0: i32, v1: i32):
|
||||
[Op1icscc#39,%rdx] v2 = icmp slt v0, v1
|
||||
[Op1t8jccd_long#85] brnz v2, ebb1
|
||||
[Op1ret#c3] return v1
|
||||
|
||||
ebb1:
|
||||
[Op1puid#b8,%rax] v8 = iconst.i32 3
|
||||
[Op1ret#c3] return v8
|
||||
}
|
||||
; sameln: function %br_icmp
|
||||
; nextln: ebb0(v0: i32, v1: i32):
|
||||
; nextln: v9 = ifcmp v0, v1
|
||||
; nextln: v2 = trueif slt v9
|
||||
; nextln: brif slt v9, ebb1
|
||||
; nextln: return v1
|
||||
; nextln:
|
||||
; nextln: ebb1:
|
||||
; nextln: v8 = iconst.i32 3
|
||||
; nextln: return v8
|
||||
; nextln: }
|
||||
|
||||
; Use brz instead of brnz, so the condition is inverted.
|
||||
|
||||
function %br_icmp_inverse(i32, i32) -> i32 {
|
||||
ebb0(v0: i32, v1: i32):
|
||||
[Op1icscc#39,%rdx] v2 = icmp slt v0, v1
|
||||
[Op1t8jccd_long#84] brz v2, ebb1
|
||||
[Op1ret#c3] return v1
|
||||
|
||||
ebb1:
|
||||
[Op1puid#b8,%rax] v8 = iconst.i32 3
|
||||
[Op1ret#c3] return v8
|
||||
}
|
||||
; sameln: function %br_icmp_inverse
|
||||
; nextln: ebb0(v0: i32, v1: i32):
|
||||
; nextln: v9 = ifcmp v0, v1
|
||||
; nextln: v2 = trueif slt v9
|
||||
; nextln: brif sge v9, ebb1
|
||||
; nextln: return v1
|
||||
; nextln:
|
||||
; nextln: ebb1:
|
||||
; nextln: v8 = iconst.i32 3
|
||||
; nextln: return v8
|
||||
; nextln: }
|
||||
|
||||
; Use icmp_imm instead of icmp.
|
||||
|
||||
function %br_icmp_imm(i32, i32) -> i32 {
|
||||
ebb0(v0: i32, v1: i32):
|
||||
[Op1icsccib#7083] v2 = icmp_imm slt v0, 2
|
||||
[Op1t8jccd_long#84] brz v2, ebb1
|
||||
[Op1ret#c3] return v1
|
||||
|
||||
ebb1:
|
||||
[Op1puid#b8,%rax] v8 = iconst.i32 3
|
||||
[Op1ret#c3] return v8
|
||||
}
|
||||
; sameln: function %br_icmp_imm
|
||||
; nextln: ebb0(v0: i32, v1: i32):
|
||||
; nextln: v9 = ifcmp_imm v0, 2
|
||||
; nextln: v2 = trueif slt v9
|
||||
; nextln: brif sge v9, ebb1
|
||||
; nextln: return v1
|
||||
; nextln:
|
||||
; nextln: ebb1:
|
||||
; nextln: v8 = iconst.i32 3
|
||||
; nextln: return v8
|
||||
; nextln: }
|
||||
|
||||
; Use fcmp instead of icmp.
|
||||
|
||||
function %br_fcmp(f32, f32) -> f32 {
|
||||
ebb0(v0: f32, v1: f32):
|
||||
[Op2fcscc#42e,%rdx] v2 = fcmp gt v0, v1
|
||||
[Op1t8jccd_long#84] brz v2, ebb1
|
||||
[Op1ret#c3] return v1
|
||||
|
||||
ebb1:
|
||||
[Op1puid#b8,%rax] v18 = iconst.i32 0x40a8_0000
|
||||
[Mp2frurm#56e,%xmm0] v8 = bitcast.f32 v18
|
||||
[Op1ret#c3] return v8
|
||||
}
|
||||
; sameln: function %br_fcmp
|
||||
; nextln: ebb0(v0: f32, v1: f32):
|
||||
; nextln: v19 = ffcmp v0, v1
|
||||
; nextln: v2 = trueff gt v19
|
||||
; nextln: brff ule v19, ebb1
|
||||
; nextln: return v1
|
||||
; nextln:
|
||||
; nextln: ebb1:
|
||||
; nextln: v18 = iconst.i32 0x40a8_0000
|
||||
; nextln: v8 = bitcast.f32 v18
|
||||
; nextln: return v8
|
||||
; nextln: }
|
||||
80
cranelift/filetests/preopt/simplify.cton
Normal file
80
cranelift/filetests/preopt/simplify.cton
Normal file
@@ -0,0 +1,80 @@
|
||||
test preopt
|
||||
isa intel
|
||||
|
||||
function %iadd_imm(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
v1 = iconst.i32 2
|
||||
v2 = iadd v0, v1
|
||||
return v2
|
||||
}
|
||||
; sameln: function %iadd_imm
|
||||
; nextln: ebb0(v0: i32):
|
||||
; nextln: v1 = iconst.i32 2
|
||||
; nextln: v2 = iadd_imm v0, 2
|
||||
; nextln: return v2
|
||||
; nextln: }
|
||||
|
||||
function %isub_imm(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
v1 = iconst.i32 2
|
||||
v2 = isub v0, v1
|
||||
return v2
|
||||
}
|
||||
; sameln: function %isub_imm
|
||||
; nextln: ebb0(v0: i32):
|
||||
; nextln: v1 = iconst.i32 2
|
||||
; nextln: v2 = iadd_imm v0, -2
|
||||
; nextln: return v2
|
||||
; nextln: }
|
||||
|
||||
function %icmp_imm(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
v1 = iconst.i32 2
|
||||
v2 = icmp slt v0, v1
|
||||
v3 = bint.i32 v2
|
||||
return v3
|
||||
}
|
||||
; sameln: function %icmp_imm
|
||||
; nextln: ebb0(v0: i32):
|
||||
; nextln: v1 = iconst.i32 2
|
||||
; nextln: v2 = icmp_imm slt v0, 2
|
||||
; nextln: v3 = bint.i32 v2
|
||||
; nextln: return v3
|
||||
; nextln: }
|
||||
|
||||
function %brz_bint(i32) {
|
||||
ebb0(v0: i32):
|
||||
v3 = icmp_imm slt v0, 0
|
||||
v1 = bint.i32 v3
|
||||
v2 = select v1, v1, v1
|
||||
trapz v1, user0
|
||||
brz v1, ebb1
|
||||
jump ebb2
|
||||
|
||||
ebb1:
|
||||
return
|
||||
|
||||
ebb2:
|
||||
return
|
||||
}
|
||||
; sameln: function %brz_bint
|
||||
; nextln: (v0: i32):
|
||||
; nextln: v3 = icmp_imm slt v0, 0
|
||||
; nextln: v1 = bint.i32 v3
|
||||
; nextln: v2 = select v3, v1, v1
|
||||
; nextln: trapz v3, user0
|
||||
; nextln: brz v3, ebb1
|
||||
; nextln: jump ebb2
|
||||
|
||||
function %irsub_imm(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
v1 = iconst.i32 2
|
||||
v2 = isub v1, v0
|
||||
return v2
|
||||
}
|
||||
; sameln: function %irsub_imm
|
||||
; nextln: ebb0(v0: i32):
|
||||
; nextln: v1 = iconst.i32 2
|
||||
; nextln: v2 = irsub_imm v1, 2
|
||||
; nextln: return v2
|
||||
; nextln: }
|
||||
@@ -21,7 +21,7 @@ function %pr227(i32 [%rdi], i32 [%rsi], i32 [%rdx], i32 [%rcx], i64 vmctx [%r8])
|
||||
@0011 [RexOp1puid#b8] v9 = iconst.i32 0
|
||||
@0015 [RexOp1puid#b8] v11 = iconst.i32 0
|
||||
@0017 [RexOp1icscc#39] v12 = icmp.i32 eq v15, v11
|
||||
@0017 [RexOp2urm#4b6] v13 = bint.i32 v12
|
||||
@0017 [RexOp2urm_noflags#4b6] v13 = bint.i32 v12
|
||||
@001a [RexOp1rr#21] v14 = band v9, v13
|
||||
@001b [RexOp1tjccb#75] brnz v14, ebb6
|
||||
@001d [RexOp1jmpb#eb] jump ebb7
|
||||
|
||||
@@ -4,65 +4,65 @@ isa intel
|
||||
; Simple, correct use of CPU flags.
|
||||
function %simple(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op2urm_abcd#4b6] v3 = bint.i32 v2
|
||||
[Op1ret#c3] return v3
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op2urm_noflags_abcd#4b6] v3 = bint.i32 v2
|
||||
[Op1ret#c3] return v3
|
||||
}
|
||||
|
||||
; Overlapping flag values of different types.
|
||||
function %overlap(i32, f32) -> i32 {
|
||||
ebb0(v0: i32, v1: f32):
|
||||
[Op1rcmp#39] v2 = ifcmp v0, v0
|
||||
[Op2fcmp#42e] v3 = ffcmp v1, v1
|
||||
[Op2setf_abcd#490] v4 = trueff gt v3 ; error: conflicting live CPU flags: v2 and v3
|
||||
[Op2seti_abcd#490] v5 = trueif ugt v2
|
||||
[Op1rr#21] v6 = band v4, v5
|
||||
[Op2urm_abcd#4b6] v7 = bint.i32 v6
|
||||
[Op1ret#c3] return v7
|
||||
[Op1rcmp#39] v2 = ifcmp v0, v0
|
||||
[Op2fcmp#42e] v3 = ffcmp v1, v1
|
||||
[Op2setf_abcd#490] v4 = trueff gt v3 ; error: conflicting live CPU flags: v2 and v3
|
||||
[Op2seti_abcd#490] v5 = trueif ugt v2
|
||||
[Op1rr#21] v6 = band v4, v5
|
||||
[Op2urm_noflags_abcd#4b6] v7 = bint.i32 v6
|
||||
[Op1ret#c3] return v7
|
||||
}
|
||||
|
||||
; CPU flags clobbered by arithmetic.
|
||||
function %clobbered(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1rr#01] v2 = iadd v0, v0 ; error: encoding clobbers live CPU flags in v1
|
||||
[Op2seti_abcd#490] v3 = trueif ugt v1
|
||||
[Op2urm_abcd#4b6] v4 = bint.i32 v3
|
||||
[Op1ret#c3] return v4
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1rr#01] v2 = iadd v0, v0 ; error: encoding clobbers live CPU flags in v1
|
||||
[Op2seti_abcd#490] v3 = trueif ugt v1
|
||||
[Op2urm_noflags_abcd#4b6] v4 = bint.i32 v3
|
||||
[Op1ret#c3] return v4
|
||||
}
|
||||
|
||||
; CPU flags not clobbered by load.
|
||||
function %live_across_load(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1ld#8b] v2 = load.i32 v0
|
||||
[Op2seti_abcd#490] v3 = trueif ugt v1
|
||||
[Op2urm_abcd#4b6] v4 = bint.i32 v3
|
||||
[Op1ret#c3] return v4
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1ld#8b] v2 = load.i32 v0
|
||||
[Op2seti_abcd#490] v3 = trueif ugt v1
|
||||
[Op2urm_noflags_abcd#4b6] v4 = bint.i32 v3
|
||||
[Op1ret#c3] return v4
|
||||
}
|
||||
|
||||
; Correct use of CPU flags across EBB.
|
||||
function %live_across_ebb(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1jmpb#eb] jump ebb1
|
||||
ebb1:
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op2urm_abcd#4b6] v3 = bint.i32 v2
|
||||
[Op1ret#c3] return v3
|
||||
ebb0(v0: i32):
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1jmpb#eb] jump ebb1
|
||||
ebb1:
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op2urm_noflags_abcd#4b6] v3 = bint.i32 v2
|
||||
[Op1ret#c3] return v3
|
||||
}
|
||||
|
||||
function %live_across_ebb_backwards(i32) -> i32 {
|
||||
ebb0(v0: i32):
|
||||
[Op1jmpb#eb] jump ebb2
|
||||
ebb1:
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op2urm_abcd#4b6] v3 = bint.i32 v2
|
||||
[Op1ret#c3] return v3
|
||||
ebb2:
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1jmpb#eb] jump ebb1
|
||||
ebb0(v0: i32):
|
||||
[Op1jmpb#eb] jump ebb2
|
||||
ebb1:
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op2urm_noflags_abcd#4b6] v3 = bint.i32 v2
|
||||
[Op1ret#c3] return v3
|
||||
ebb2:
|
||||
[Op1rcmp#39] v1 = ifcmp v0, v0
|
||||
[Op1jmpb#eb] jump ebb1
|
||||
}
|
||||
|
||||
; Flags live into loop.
|
||||
@@ -73,4 +73,4 @@ function %live_into_loop(i32) -> i32 {
|
||||
ebb1:
|
||||
[Op2seti_abcd#490] v2 = trueif ugt v1
|
||||
[Op1jmpb#eb] jump ebb1
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user