# RUN: llc -run-pass x86-flags-copy-lowering -verify-machineinstrs -o - %s | FileCheck %s # # Lower various interesting copy patterns of EFLAGS without using LAHF/SAHF. --- | target triple = "x86_64-unknown-unknown" declare void @foo() define i32 @test_branch(i64 %a, i64 %b) { entry: call void @foo() ret i32 0 } define i32 @test_branch_fallthrough(i64 %a, i64 %b) { entry: call void @foo() ret i32 0 } define void @test_setcc(i64 %a, i64 %b) { entry: call void @foo() ret void } define void @test_cmov(i64 %a, i64 %b) { entry: call void @foo() ret void } define void @test_adc(i64 %a, i64 %b) { entry: call void @foo() ret void } define void @test_sbb(i64 %a, i64 %b) { entry: call void @foo() ret void } define void @test_adcx(i64 %a, i64 %b) { entry: call void @foo() ret void } define void @test_adox(i64 %a, i64 %b) { entry: call void @foo() ret void } define void @test_rcl(i64 %a, i64 %b) { entry: call void @foo() ret void } define void @test_rcr(i64 %a, i64 %b) { entry: call void @foo() ret void } define void @test_setb_c(i64 %a, i64 %b) { entry: call void @foo() ret void } define i64 @test_branch_with_livein_and_kill(i64 %a, i64 %b) { entry: call void @foo() ret i64 0 } define i64 @test_branch_with_interleaved_livein_and_kill(i64 %a, i64 %b) { entry: call void @foo() ret i64 0 } define i64 @test_mid_cycle_copies(i64 %a, i64 %b) { entry: call void @foo() ret i64 0 } define i32 @test_existing_setcc(i64 %a, i64 %b) { entry: call void @foo() ret i32 0 } define i32 @test_existing_setcc_memory(i64 %a, i64 %b) { entry: call void @foo() ret i32 0 } ... --- name: test_branch # CHECK-LABEL: name: test_branch liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: successors: %bb.1, %bb.2, %bb.3 liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi CMP64rr %0, %1, implicit-def $eflags %2:gr64 = COPY $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[A_REG:[^:]*]]:gr8 = SETCCr 7, implicit $eflags ; CHECK-NEXT: %[[B_REG:[^:]*]]:gr8 = SETCCr 2, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %2 JCC_1 %bb.1, 7, implicit $eflags JCC_1 %bb.2, 2, implicit $eflags JMP_1 %bb.3 ; CHECK-NOT: $eflags = ; ; CHECK: TEST8rr %[[A_REG]], %[[A_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.1, 5, implicit killed $eflags ; CHECK-SAME: {{$[[:space:]]}} ; CHECK-NEXT: bb.4: ; CHECK-NEXT: successors: {{.*$}} ; CHECK-SAME: {{$[[:space:]]}} ; CHECK-NEXT: TEST8rr %[[B_REG]], %[[B_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.2, 5, implicit killed $eflags ; CHECK-NEXT: JMP_1 %bb.3 bb.1: %3:gr32 = MOV32ri 42 $eax = COPY %3 RET 0, $eax bb.2: %4:gr32 = MOV32ri 43 $eax = COPY %4 RET 0, $eax bb.3: %5:gr32 = MOV32r0 implicit-def dead $eflags $eax = COPY %5 RET 0, $eax ... --- name: test_branch_fallthrough # CHECK-LABEL: name: test_branch_fallthrough liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: successors: %bb.1, %bb.2, %bb.3 liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi CMP64rr %0, %1, implicit-def $eflags %2:gr64 = COPY $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[A_REG:[^:]*]]:gr8 = SETCCr 7, implicit $eflags ; CHECK-NEXT: %[[B_REG:[^:]*]]:gr8 = SETCCr 2, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %2 JCC_1 %bb.2, 7, implicit $eflags JCC_1 %bb.3, 2, implicit $eflags ; CHECK-NOT: $eflags = ; ; CHECK: TEST8rr %[[A_REG]], %[[A_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.2, 5, implicit killed $eflags ; CHECK-SAME: {{$[[:space:]]}} ; CHECK-NEXT: bb.4: ; CHECK-NEXT: successors: {{.*$}} ; CHECK-SAME: {{$[[:space:]]}} ; CHECK-NEXT: TEST8rr %[[B_REG]], %[[B_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.3, 5, implicit killed $eflags ; CHECK-SAME: {{$[[:space:]]}} ; CHECK-NEXT: bb.1: bb.1: %5:gr32 = MOV32r0 implicit-def dead $eflags $eax = COPY %5 RET 0, $eax bb.2: %3:gr32 = MOV32ri 42 $eax = COPY %3 RET 0, $eax bb.3: %4:gr32 = MOV32ri 43 $eax = COPY %4 RET 0, $eax ... --- name: test_setcc # CHECK-LABEL: name: test_setcc liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi CMP64rr %0, %1, implicit-def $eflags %2:gr64 = COPY $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[A_REG:[^:]*]]:gr8 = SETCCr 7, implicit $eflags ; CHECK-NEXT: %[[B_REG:[^:]*]]:gr8 = SETCCr 2, implicit $eflags ; CHECK-NEXT: %[[E_REG:[^:]*]]:gr8 = SETCCr 4, implicit $eflags ; CHECK-NEXT: %[[NE_REG:[^:]*]]:gr8 = SETCCr 5, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %2 %3:gr8 = SETCCr 7, implicit $eflags %4:gr8 = SETCCr 2, implicit $eflags %5:gr8 = SETCCr 4, implicit $eflags SETCCm $rsp, 1, $noreg, -16, $noreg, 5, implicit killed $eflags MOV8mr $rsp, 1, $noreg, -16, $noreg, killed %3 MOV8mr $rsp, 1, $noreg, -16, $noreg, killed %4 MOV8mr $rsp, 1, $noreg, -16, $noreg, killed %5 ; CHECK-NOT: $eflags = ; CHECK-NOT: = SET{{.*}} ; CHECK: MOV8mr {{.*}}, %[[A_REG]] ; CHECK-NEXT: MOV8mr {{.*}}, %[[B_REG]] ; CHECK-NEXT: MOV8mr {{.*}}, %[[E_REG]] ; CHECK-NOT: MOV8mr {{.*}}, %[[NE_REG]] RET 0 ... --- name: test_cmov # CHECK-LABEL: name: test_cmov liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi CMP64rr %0, %1, implicit-def $eflags %2:gr64 = COPY $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[A_REG:[^:]*]]:gr8 = SETCCr 7, implicit $eflags ; CHECK-NEXT: %[[B_REG:[^:]*]]:gr8 = SETCCr 2, implicit $eflags ; CHECK-NEXT: %[[E_REG:[^:]*]]:gr8 = SETCCr 4, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %2 %3:gr64 = CMOV64rr %0, %1, 7, implicit $eflags %4:gr64 = CMOV64rr %0, %1, 2, implicit $eflags %5:gr64 = CMOV64rr %0, %1, 4, implicit $eflags %6:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags ; CHECK-NOT: $eflags = ; CHECK: TEST8rr %[[A_REG]], %[[A_REG]], implicit-def $eflags ; CHECK-NEXT: %3:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags ; CHECK-NEXT: TEST8rr %[[B_REG]], %[[B_REG]], implicit-def $eflags ; CHECK-NEXT: %4:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags ; CHECK-NEXT: TEST8rr %[[E_REG]], %[[E_REG]], implicit-def $eflags ; CHECK-NEXT: %5:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags ; CHECK-NEXT: TEST8rr %[[E_REG]], %[[E_REG]], implicit-def $eflags ; CHECK-NEXT: %6:gr64 = CMOV64rr %0, %1, 4, implicit killed $eflags MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %3 MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %4 MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %5 MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %6 RET 0 ... --- name: test_adc # CHECK-LABEL: name: test_adc liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi %2:gr64 = ADD64rr %0, %1, implicit-def $eflags %3:gr64 = COPY $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[CF_REG:[^:]*]]:gr8 = SETCCr 2, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %3 %4:gr64 = ADC64ri32 %2:gr64, 42, implicit-def $eflags, implicit $eflags %5:gr64 = ADC64ri32 %4:gr64, 42, implicit-def $eflags, implicit $eflags ; CHECK-NOT: $eflags = ; CHECK: dead %{{[^:]*}}:gr8 = ADD8ri %[[CF_REG]], 255, implicit-def $eflags ; CHECK-NEXT: %4:gr64 = ADC64ri32 %2, 42, implicit-def $eflags, implicit killed $eflags ; CHECK-NEXT: %5:gr64 = ADC64ri32 %4, 42, implicit-def{{( dead)?}} $eflags, implicit{{( killed)?}} $eflags MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %5 RET 0 ... --- name: test_sbb # CHECK-LABEL: name: test_sbb liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi %2:gr64 = SUB64rr %0, %1, implicit-def $eflags %3:gr64 = COPY killed $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[CF_REG:[^:]*]]:gr8 = SETCCr 2, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %3 %4:gr64 = SBB64ri32 %2:gr64, 42, implicit-def $eflags, implicit killed $eflags %5:gr64 = SBB64ri32 %4:gr64, 42, implicit-def dead $eflags, implicit killed $eflags ; CHECK-NOT: $eflags = ; CHECK: dead %{{[^:]*}}:gr8 = ADD8ri %[[CF_REG]], 255, implicit-def $eflags ; CHECK-NEXT: %4:gr64 = SBB64ri32 %2, 42, implicit-def $eflags, implicit killed $eflags ; CHECK-NEXT: %5:gr64 = SBB64ri32 %4, 42, implicit-def{{( dead)?}} $eflags, implicit{{( killed)?}} $eflags MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %5 RET 0 ... --- name: test_adcx # CHECK-LABEL: name: test_adcx liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi %2:gr64 = ADD64rr %0, %1, implicit-def $eflags %3:gr64 = COPY $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[E_REG:[^:]*]]:gr8 = SETCCr 4, implicit $eflags ; CHECK-NEXT: %[[CF_REG:[^:]*]]:gr8 = SETCCr 2, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %3 %4:gr64 = CMOV64rr %0, %1, 4, implicit $eflags %5:gr64 = MOV64ri32 42 %6:gr64 = ADCX64rr %2, %5, implicit-def $eflags, implicit $eflags ; CHECK-NOT: $eflags = ; CHECK: TEST8rr %[[E_REG]], %[[E_REG]], implicit-def $eflags ; CHECK-NEXT: %4:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags ; CHECK-NEXT: %5:gr64 = MOV64ri32 42 ; CHECK-NEXT: dead %{{[^:]*}}:gr8 = ADD8ri %[[CF_REG]], 255, implicit-def $eflags ; CHECK-NEXT: %6:gr64 = ADCX64rr %2, %5, implicit-def{{( dead)?}} $eflags, implicit killed $eflags MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %4 MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %6 RET 0 ... --- name: test_adox # CHECK-LABEL: name: test_adox liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi %2:gr64 = ADD64rr %0, %1, implicit-def $eflags %3:gr64 = COPY $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[E_REG:[^:]*]]:gr8 = SETCCr 4, implicit $eflags ; CHECK-NEXT: %[[OF_REG:[^:]*]]:gr8 = SETCCr 0, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %3 %4:gr64 = CMOV64rr %0, %1, 4, implicit $eflags %5:gr64 = MOV64ri32 42 %6:gr64 = ADOX64rr %2, %5, implicit-def $eflags, implicit $eflags ; CHECK-NOT: $eflags = ; CHECK: TEST8rr %[[E_REG]], %[[E_REG]], implicit-def $eflags ; CHECK-NEXT: %4:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags ; CHECK-NEXT: %5:gr64 = MOV64ri32 42 ; CHECK-NEXT: dead %{{[^:]*}}:gr8 = ADD8ri %[[OF_REG]], 127, implicit-def $eflags ; CHECK-NEXT: %6:gr64 = ADOX64rr %2, %5, implicit-def{{( dead)?}} $eflags, implicit killed $eflags MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %4 MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %6 RET 0 ... --- name: test_rcl # CHECK-LABEL: name: test_rcl liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi %2:gr64 = ADD64rr %0, %1, implicit-def $eflags %3:gr64 = COPY $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[CF_REG:[^:]*]]:gr8 = SETCCr 2, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %3 %4:gr64 = RCL64r1 %2:gr64, implicit-def $eflags, implicit $eflags %5:gr64 = RCL64r1 %4:gr64, implicit-def $eflags, implicit $eflags ; CHECK-NOT: $eflags = ; CHECK: dead %{{[^:]*}}:gr8 = ADD8ri %[[CF_REG]], 255, implicit-def $eflags ; CHECK-NEXT: %4:gr64 = RCL64r1 %2, implicit-def $eflags, implicit killed $eflags ; CHECK-NEXT: %5:gr64 = RCL64r1 %4, implicit-def{{( dead)?}} $eflags, implicit{{( killed)?}} $eflags MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %5 RET 0 ... --- name: test_rcr # CHECK-LABEL: name: test_rcr liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi %2:gr64 = ADD64rr %0, %1, implicit-def $eflags %3:gr64 = COPY $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[CF_REG:[^:]*]]:gr8 = SETCCr 2, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %3 %4:gr64 = RCR64r1 %2:gr64, implicit-def $eflags, implicit $eflags %5:gr64 = RCR64r1 %4:gr64, implicit-def $eflags, implicit $eflags ; CHECK-NOT: $eflags = ; CHECK: dead %{{[^:]*}}:gr8 = ADD8ri %[[CF_REG]], 255, implicit-def $eflags ; CHECK-NEXT: %4:gr64 = RCR64r1 %2, implicit-def $eflags, implicit killed $eflags ; CHECK-NEXT: %5:gr64 = RCR64r1 %4, implicit-def{{( dead)?}} $eflags, implicit{{( killed)?}} $eflags MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %5 RET 0 ... --- name: test_setb_c # CHECK-LABEL: name: test_setb_c liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi %2:gr64 = ADD64rr %0, %1, implicit-def $eflags %3:gr64 = COPY $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[CF_REG:[^:]*]]:gr8 = SETCCr 2, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %3 %4:gr32 = SETB_C32r implicit-def $eflags, implicit $eflags MOV32mr $rsp, 1, $noreg, -16, $noreg, killed %4 ; CHECK-NOT: $eflags = ; CHECK: dead %{{[^:]*}}:gr8 = ADD8ri %[[CF_REG]], 255, implicit-def $eflags ; CHECK-NEXT: %[[SETB:[^:]*]]:gr32 = SETB_C32r implicit-def{{( dead)?}} $eflags, implicit{{( killed)?}} $eflags ; CHECK-NEXT: MOV32mr $rsp, 1, $noreg, -16, $noreg, killed %[[SETB]] $eflags = COPY %3 %5:gr64 = SETB_C64r implicit-def $eflags, implicit $eflags MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %5 ; CHECK-NOT: $eflags = ; CHECK: dead %{{[^:]*}}:gr8 = ADD8ri %[[CF_REG]], 255, implicit-def $eflags ; CHECK-NEXT: %[[SETB:[^:]*]]:gr64 = SETB_C64r implicit-def{{( dead)?}} $eflags, implicit{{( killed)?}} $eflags ; CHECK-NEXT: MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %[[SETB]] RET 0 ... --- name: test_branch_with_livein_and_kill # CHECK-LABEL: name: test_branch_with_livein_and_kill liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: successors: %bb.1, %bb.2, %bb.3 liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi CMP64rr %0, %1, implicit-def $eflags %2:gr64 = COPY $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[S_REG:[^:]*]]:gr8 = SETCCr 8, implicit $eflags ; CHECK-NEXT: %[[NE_REG:[^:]*]]:gr8 = SETCCr 5, implicit $eflags ; CHECK-NEXT: %[[A_REG:[^:]*]]:gr8 = SETCCr 7, implicit $eflags ; CHECK-NEXT: %[[B_REG:[^:]*]]:gr8 = SETCCr 2, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %2 JCC_1 %bb.1, 7, implicit $eflags JCC_1 %bb.2, 2, implicit $eflags JMP_1 %bb.3 ; CHECK-NOT: $eflags = ; ; CHECK: TEST8rr %[[A_REG]], %[[A_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.1, 5, implicit killed $eflags ; CHECK-SAME: {{$[[:space:]]}} ; CHECK-NEXT: bb.4: ; CHECK-NEXT: successors: {{.*$}} ; CHECK-SAME: {{$[[:space:]]}} ; CHECK-NEXT: TEST8rr %[[B_REG]], %[[B_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.2, 5, implicit killed $eflags ; CHECK-NEXT: JMP_1 %bb.3 bb.1: liveins: $eflags %3:gr64 = CMOV64rr %0, %1, 4, implicit killed $eflags ; CHECK-NOT: $eflags = ; CHECK: TEST8rr %[[NE_REG]], %[[NE_REG]], implicit-def $eflags ; CHECK-NEXT: %3:gr64 = CMOV64rr %0, %1, 4, implicit killed $eflags $rax = COPY %3 RET 0, $rax bb.2: liveins: $eflags %4:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags ; CHECK-NOT: $eflags = ; CHECK: TEST8rr %[[NE_REG]], %[[NE_REG]], implicit-def $eflags ; CHECK-NEXT: %4:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags $rax = COPY %4 RET 0, $rax bb.3: liveins: $eflags %5:gr64 = CMOV64rr %0, %1, 8, implicit killed $eflags ; CHECK-NOT: $eflags = ; CHECK: TEST8rr %[[S_REG]], %[[S_REG]], implicit-def $eflags ; CHECK-NEXT: %5:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags $rax = COPY %5 RET 0, $rax ... --- name: test_branch_with_interleaved_livein_and_kill # CHECK-LABEL: name: test_branch_with_interleaved_livein_and_kill liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: successors: %bb.1, %bb.2, %bb.5 liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi CMP64rr %0, %1, implicit-def $eflags %2:gr64 = COPY $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[S_REG:[^:]*]]:gr8 = SETCCr 8, implicit $eflags ; CHECK-NEXT: %[[P_REG:[^:]*]]:gr8 = SETCCr 10, implicit $eflags ; CHECK-NEXT: %[[NE_REG:[^:]*]]:gr8 = SETCCr 5, implicit $eflags ; CHECK-NEXT: %[[A_REG:[^:]*]]:gr8 = SETCCr 7, implicit $eflags ; CHECK-NEXT: %[[B_REG:[^:]*]]:gr8 = SETCCr 2, implicit $eflags ; CHECK-NEXT: %[[O_REG:[^:]*]]:gr8 = SETCCr 0, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %2 JCC_1 %bb.1, 7, implicit $eflags JCC_1 %bb.2, 2, implicit $eflags JMP_1 %bb.5 ; CHECK-NOT: $eflags = ; ; CHECK: TEST8rr %[[A_REG]], %[[A_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.1, 5, implicit killed $eflags ; CHECK-SAME: {{$[[:space:]]}} ; CHECK-NEXT: bb.6: ; CHECK-NEXT: successors: {{.*$}} ; CHECK-SAME: {{$[[:space:]]}} ; CHECK-NEXT: TEST8rr %[[B_REG]], %[[B_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.2, 5, implicit killed $eflags ; CHECK-NEXT: JMP_1 %bb.5 bb.1: liveins: $eflags %3:gr64 = CMOV64rr %0, %1, 4, implicit killed $eflags ; CHECK-NOT: $eflags = ; CHECK: TEST8rr %[[NE_REG]], %[[NE_REG]], implicit-def $eflags ; CHECK-NEXT: %3:gr64 = CMOV64rr %0, %1, 4, implicit killed $eflags $rax = COPY %3 RET 0, $rax bb.2: ; The goal is to have another batch of successors discovered in a block ; between two successors which kill $eflags. This ensures that neither of ; the surrounding kills impact recursing through this block. successors: %bb.3, %bb.4 liveins: $eflags JCC_1 %bb.3, 0, implicit $eflags JMP_1 %bb.4 ; CHECK-NOT: $eflags = ; ; CHECK: TEST8rr %[[O_REG]], %[[O_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.3, 5, implicit killed $eflags ; CHECK-NEXT: JMP_1 %bb.4 bb.3: liveins: $eflags %4:gr64 = CMOV64rr %0, %1, 5, implicit $eflags ; CHECK-NOT: $eflags = ; CHECK: TEST8rr %[[NE_REG]], %[[NE_REG]], implicit-def $eflags ; CHECK-NEXT: %4:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags $rax = COPY %4 RET 0, $rax bb.4: liveins: $eflags %5:gr64 = CMOV64rr %0, %1, 10, implicit $eflags ; CHECK-NOT: $eflags = ; CHECK: TEST8rr %[[P_REG]], %[[P_REG]], implicit-def $eflags ; CHECK-NEXT: %5:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags $rax = COPY %5 RET 0, $rax bb.5: liveins: $eflags %6:gr64 = CMOV64rr %0, %1, 8, implicit killed $eflags ; CHECK-NOT: $eflags = ; CHECK: TEST8rr %[[S_REG]], %[[S_REG]], implicit-def $eflags ; CHECK-NEXT: %6:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags $rax = COPY %6 RET 0, $rax ... --- # This test case is designed to exercise a particularly challenging situation: # when the flags are copied and restored *inside* of a complex and cyclic CFG # all of which have live-in flags. To correctly handle this case we have to walk # up the dominator tree and locate a viable reaching definition location, # checking for clobbers along any path. The CFG for this function looks like the # following diagram, control flowing out the bottom of blocks and in the top: # # bb.0 # | __________________ # |/ \ # bb.1 | # |\_________ | # | __ \ ____ | # |/ \ |/ \ | # bb.2 | bb.4 | | # |\__/ / \ | | # | / \ | | # bb.3 bb.5 bb.6 | | # | \ / | | # | \ / | | # | bb.7 | | # | ________/ \____/ | # |/ | # bb.8 | # |\__________________/ # | # bb.9 # # We set EFLAGS in bb.0, clobber them in bb.3, and copy them in bb.2 and bb.6. # Because of the cycles this requires hoisting the `SETcc` instructions to # capture the flags for the bb.6 copy to bb.1 and using them for the copy in # `bb.2` as well despite the clobber in `bb.3`. The clobber in `bb.3` also # prevents hoisting the `SETcc`s up to `bb.0`. # # Throughout the test we use branch instructions that are totally bogus (as the # flags are obviously not changing!) but this is just to allow us to send # a small but complex CFG structure through the backend and force it to choose # plausible lowering decisions based on the core CFG presented, regardless of # the futility of the actual branches. name: test_mid_cycle_copies # CHECK-LABEL: name: test_mid_cycle_copies liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: successors: %bb.1 liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi CMP64rr %0, %1, implicit-def $eflags ; CHECK: bb.0: ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: CMP64rr %0, %1, implicit-def $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags JMP_1 %bb.1 bb.1: successors: %bb.2, %bb.4 liveins: $eflags ; Outer loop header, target for one set of hoisting. JCC_1 %bb.2, 4, implicit $eflags JMP_1 %bb.4 ; CHECK: bb.1: ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: %[[A_REG:[^:]*]]:gr8 = SETCCr 7, implicit $eflags ; CHECK-NEXT: %[[E_REG:[^:]*]]:gr8 = SETCCr 4, implicit $eflags ; CHECK-NEXT: %[[B_REG:[^:]*]]:gr8 = SETCCr 2, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags bb.2: successors: %bb.2, %bb.3 liveins: $eflags ; Inner loop with a local copy. We should eliminate this but can't hoist. %2:gr64 = COPY $eflags $eflags = COPY %2 JCC_1 %bb.2, 4, implicit $eflags JMP_1 %bb.3 ; CHECK: bb.2: ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: TEST8rr %[[E_REG]], %[[E_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.2, 5, implicit killed $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags bb.3: successors: %bb.8 liveins: $eflags ; Use and then clobber $eflags. Then hop to the outer loop latch. %3:gr64 = ADC64ri32 %0, 42, implicit-def dead $eflags, implicit $eflags ; CHECK: bb.3: ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: dead %{{[^:]*}}:gr8 = ADD8ri %[[B_REG]], 255, implicit-def $eflags ; CHECK-NEXT: %3:gr64 = ADC64ri32 %0, 42, implicit-def{{( dead)?}} $eflags, implicit{{( killed)?}} $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %3 JMP_1 %bb.8 bb.4: successors: %bb.5, %bb.6 liveins: $eflags ; Another inner loop, this one with a diamond. JCC_1 %bb.5, 4, implicit $eflags JMP_1 %bb.6 ; CHECK: bb.4: ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: TEST8rr %[[E_REG]], %[[E_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.5, 5, implicit killed $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags bb.5: successors: %bb.7 liveins: $eflags ; Just use $eflags on this side of the diamond. %4:gr64 = CMOV64rr %0, %1, 7, implicit $eflags ; CHECK: bb.5: ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: TEST8rr %[[A_REG]], %[[A_REG]], implicit-def $eflags ; CHECK-NEXT: %4:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %4 JMP_1 %bb.7 bb.6: successors: %bb.7 liveins: $eflags ; Use, copy, and then use $eflags again. %5:gr64 = CMOV64rr %0, %1, 7, implicit $eflags ; CHECK: bb.6: ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: TEST8rr %[[A_REG]], %[[A_REG]], implicit-def $eflags ; CHECK-NEXT: %5:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %5 %6:gr64 = COPY $eflags $eflags = COPY %6:gr64 %7:gr64 = CMOV64rr %0, %1, 7, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: TEST8rr %[[A_REG]], %[[A_REG]], implicit-def $eflags ; CHECK-NEXT: %7:gr64 = CMOV64rr %0, %1, 5, implicit killed $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags MOV64mr $rsp, 1, $noreg, -16, $noreg, killed %7 JMP_1 %bb.7 bb.7: successors: %bb.4, %bb.8 liveins: $eflags ; Inner loop latch. JCC_1 %bb.4, 4, implicit $eflags JMP_1 %bb.8 ; CHECK: bb.7: ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: TEST8rr %[[E_REG]], %[[E_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.4, 5, implicit killed $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags bb.8: successors: %bb.1, %bb.9 ; Outer loop latch. Note that we cannot have EFLAGS live-in here as that ; immediately require PHIs. CMP64rr %0, %1, implicit-def $eflags JCC_1 %bb.1, 4, implicit $eflags JMP_1 %bb.9 ; CHECK: bb.8: ; CHECK-NOT: COPY{{( killed)?}} $eflags ; CHECK: CMP64rr %0, %1, implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.1, 4, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags bb.9: liveins: $eflags ; And we're done. %8:gr64 = CMOV64rr %0, %1, 4, implicit killed $eflags $rax = COPY %8 RET 0, $rax ; CHECK: bb.9: ; CHECK-NEXT: liveins: $eflags ; CHECK-NOT: $eflags ; CHECK: %8:gr64 = CMOV64rr %0, %1, 4, implicit killed $eflags ... --- name: test_existing_setcc # CHECK-LABEL: name: test_existing_setcc liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: successors: %bb.1, %bb.2, %bb.3 liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi CMP64rr %0, %1, implicit-def $eflags %2:gr8 = SETCCr 7, implicit $eflags %3:gr8 = SETCCr 3, implicit $eflags %4:gr64 = COPY $eflags ; CHECK: CMP64rr %0, %1, implicit-def $eflags ; CHECK-NEXT: %[[A_REG:[^:]*]]:gr8 = SETCCr 7, implicit $eflags ; CHECK-NEXT: %[[AE_REG:[^:]*]]:gr8 = SETCCr 3, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %4 JCC_1 %bb.1, 7, implicit $eflags JCC_1 %bb.2, 2, implicit $eflags JMP_1 %bb.3 ; CHECK-NOT: $eflags = ; ; CHECK: TEST8rr %[[A_REG]], %[[A_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.1, 5, implicit killed $eflags ; CHECK-SAME: {{$[[:space:]]}} ; CHECK-NEXT: bb.4: ; CHECK-NEXT: successors: {{.*$}} ; CHECK-SAME: {{$[[:space:]]}} ; CHECK-NEXT: TEST8rr %[[AE_REG]], %[[AE_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.2, 4, implicit killed $eflags ; CHECK-NEXT: JMP_1 %bb.3 bb.1: %5:gr32 = MOV32ri 42 $eax = COPY %5 RET 0, $eax bb.2: %6:gr32 = MOV32ri 43 $eax = COPY %6 RET 0, $eax bb.3: %7:gr32 = MOV32r0 implicit-def dead $eflags $eax = COPY %7 RET 0, $eax ... --- name: test_existing_setcc_memory # CHECK-LABEL: name: test_existing_setcc_memory liveins: - { reg: '$rdi', virtual-reg: '%0' } - { reg: '$rsi', virtual-reg: '%1' } body: | bb.0: successors: %bb.1, %bb.2 liveins: $rdi, $rsi %0:gr64 = COPY $rdi %1:gr64 = COPY $rsi CMP64rr %0, %1, implicit-def $eflags SETCCm %0, 1, $noreg, -16, $noreg, 4, implicit $eflags %2:gr64 = COPY $eflags ; CHECK: CMP64rr %0, %1, implicit-def $eflags ; We cannot reuse this SETE because it stores the flag directly to memory, ; so we have two SETEs here. FIXME: It'd be great if something could fold ; these automatically. If not, maybe we want to unfold SETcc instructions ; writing to memory so we can reuse them. ; CHECK-NEXT: SETCCm {{.*}} 4, implicit $eflags ; CHECK-NEXT: %[[E_REG:[^:]*]]:gr8 = SETCCr 4, implicit $eflags ; CHECK-NOT: COPY{{( killed)?}} $eflags ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp $eflags = COPY %2 JCC_1 %bb.1, 4, implicit $eflags JMP_1 %bb.2 ; CHECK-NOT: $eflags = ; ; CHECK: TEST8rr %[[E_REG]], %[[E_REG]], implicit-def $eflags ; CHECK-NEXT: JCC_1 %bb.1, 5, implicit killed $eflags ; CHECK-NEXT: JMP_1 %bb.2 bb.1: %3:gr32 = MOV32ri 42 $eax = COPY %3 RET 0, $eax bb.2: %4:gr32 = MOV32ri 43 $eax = COPY %4 RET 0, $eax ...