| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py | |
| ; RUN: llc -mtriple=bpfel -mcpu=v3 -verify-machineinstrs < %s | FileCheck %s | |
| ; Reg-reg ADD, OR, AND, XOR and MUL are commutable, in both the 64-bit and the | |
| ; 32-bit (alu32) forms. Each loop loads one value and folds it into one | |
| ; accumulator per operation, with the accumulator as the second operand. | |
| ; Without commuting, the two-address form would copy the accumulators and the | |
| ; loaded value in every iteration. SUB is not commutable, so its copies stay. | |
| define i64 @add_or_and_i64(ptr %p, i64 %n) { | |
| ; CHECK-LABEL: add_or_and_i64: | |
| ; CHECK: # %bb.0: # %entry | |
| ; CHECK-NEXT: r3 = -1 | |
| ; CHECK-NEXT: r0 = 0 | |
| ; CHECK-NEXT: r5 = 0 | |
| ; CHECK-NEXT: r4 = 0 | |
| ; CHECK-NEXT: .LBB0_1: # %loop | |
| ; CHECK-NEXT: # =>This Inner Loop Header: Depth=1 | |
| ; CHECK-NEXT: r6 = *(u64 *)(r1 + 0) | |
| ; CHECK-NEXT: r3 &= r6 | |
| ; CHECK-NEXT: r4 |= r6 | |
| ; CHECK-NEXT: r0 += r6 | |
| ; CHECK-NEXT: r1 += 8 | |
| ; CHECK-NEXT: r5 += 1 | |
| ; CHECK-NEXT: if r5 < r2 goto .LBB0_1 | |
| ; CHECK-NEXT: # %bb.2: # %exit | |
| ; CHECK-NEXT: r0 ^= r4 | |
| ; CHECK-NEXT: r0 ^= r3 | |
| ; CHECK-NEXT: exit | |
| entry: | |
| br label %loop | |
| loop: | |
| %i = phi i64 [ 0, %entry ], [ %i.next, %loop ] | |
| %add = phi i64 [ 0, %entry ], [ %add.next, %loop ] | |
| %or = phi i64 [ 0, %entry ], [ %or.next, %loop ] | |
| %and = phi i64 [ -1, %entry ], [ %and.next, %loop ] | |
| %a = getelementptr inbounds i64, ptr %p, i64 %i | |
| %v = load i64, ptr %a | |
| %add.next = add i64 %v, %add | |
| %or.next = or i64 %v, %or | |
| %and.next = and i64 %v, %and | |
| %i.next = add i64 %i, 1 | |
| %c = icmp ult i64 %i.next, %n | |
| br i1 %c, label %loop, label %exit | |
| exit: | |
| %r0 = xor i64 %add.next, %or.next | |
| %r1 = xor i64 %r0, %and.next | |
| ret i64 %r1 | |
| } | |
| define i64 @xor_mul_sub_i64(ptr %p, i64 %n) { | |
| ; CHECK-LABEL: xor_mul_sub_i64: | |
| ; CHECK: # %bb.0: # %entry | |
| ; CHECK-NEXT: r3 = 1 | |
| ; CHECK-NEXT: r0 = 0 | |
| ; CHECK-NEXT: r4 = 0 | |
| ; CHECK-NEXT: r5 = 0 | |
| ; CHECK-NEXT: .LBB1_1: # %loop | |
| ; CHECK-NEXT: # =>This Inner Loop Header: Depth=1 | |
| ; CHECK-NEXT: r6 = r5 | |
| ; CHECK-NEXT: r7 = *(u64 *)(r1 + 0) | |
| ; CHECK-NEXT: r5 = r7 | |
| ; CHECK-NEXT: r5 -= r6 | |
| ; CHECK-NEXT: r3 *= r7 | |
| ; CHECK-NEXT: r0 ^= r7 | |
| ; CHECK-NEXT: r1 += 8 | |
| ; CHECK-NEXT: r4 += 1 | |
| ; CHECK-NEXT: if r4 < r2 goto .LBB1_1 | |
| ; CHECK-NEXT: # %bb.2: # %exit | |
| ; CHECK-NEXT: r0 ^= r3 | |
| ; CHECK-NEXT: r0 ^= r5 | |
| ; CHECK-NEXT: exit | |
| entry: | |
| br label %loop | |
| loop: | |
| %i = phi i64 [ 0, %entry ], [ %i.next, %loop ] | |
| %xor = phi i64 [ 0, %entry ], [ %xor.next, %loop ] | |
| %mul = phi i64 [ 1, %entry ], [ %mul.next, %loop ] | |
| %sub = phi i64 [ 0, %entry ], [ %sub.next, %loop ] | |
| %a = getelementptr inbounds i64, ptr %p, i64 %i | |
| %v = load i64, ptr %a | |
| %xor.next = xor i64 %v, %xor | |
| %mul.next = mul i64 %v, %mul | |
| %sub.next = sub i64 %v, %sub | |
| %i.next = add i64 %i, 1 | |
| %c = icmp ult i64 %i.next, %n | |
| br i1 %c, label %loop, label %exit | |
| exit: | |
| %r0 = xor i64 %xor.next, %mul.next | |
| %r1 = xor i64 %r0, %sub.next | |
| ret i64 %r1 | |
| } | |
| define i32 @add_or_and_i32(ptr %p, i64 %n) { | |
| ; CHECK-LABEL: add_or_and_i32: | |
| ; CHECK: # %bb.0: # %entry | |
| ; CHECK-NEXT: w3 = -1 | |
| ; CHECK-NEXT: w0 = 0 | |
| ; CHECK-NEXT: r4 = 0 | |
| ; CHECK-NEXT: w5 = 0 | |
| ; CHECK-NEXT: .LBB2_1: # %loop | |
| ; CHECK-NEXT: # =>This Inner Loop Header: Depth=1 | |
| ; CHECK-NEXT: w6 = *(u32 *)(r1 + 0) | |
| ; CHECK-NEXT: w3 &= w6 | |
| ; CHECK-NEXT: w5 |= w6 | |
| ; CHECK-NEXT: w0 += w6 | |
| ; CHECK-NEXT: r1 += 4 | |
| ; CHECK-NEXT: r4 += 1 | |
| ; CHECK-NEXT: if r4 < r2 goto .LBB2_1 | |
| ; CHECK-NEXT: # %bb.2: # %exit | |
| ; CHECK-NEXT: w0 ^= w5 | |
| ; CHECK-NEXT: w0 ^= w3 | |
| ; CHECK-NEXT: exit | |
| entry: | |
| br label %loop | |
| loop: | |
| %i = phi i64 [ 0, %entry ], [ %i.next, %loop ] | |
| %add = phi i32 [ 0, %entry ], [ %add.next, %loop ] | |
| %or = phi i32 [ 0, %entry ], [ %or.next, %loop ] | |
| %and = phi i32 [ -1, %entry ], [ %and.next, %loop ] | |
| %a = getelementptr inbounds i32, ptr %p, i64 %i | |
| %v = load i32, ptr %a | |
| %add.next = add i32 %v, %add | |
| %or.next = or i32 %v, %or | |
| %and.next = and i32 %v, %and | |
| %i.next = add i64 %i, 1 | |
| %c = icmp ult i64 %i.next, %n | |
| br i1 %c, label %loop, label %exit | |
| exit: | |
| %r0 = xor i32 %add.next, %or.next | |
| %r1 = xor i32 %r0, %and.next | |
| ret i32 %r1 | |
| } | |
| define i32 @xor_mul_sub_i32(ptr %p, i64 %n) { | |
| ; CHECK-LABEL: xor_mul_sub_i32: | |
| ; CHECK: # %bb.0: # %entry | |
| ; CHECK-NEXT: w3 = 1 | |
| ; CHECK-NEXT: w0 = 0 | |
| ; CHECK-NEXT: r4 = 0 | |
| ; CHECK-NEXT: w5 = 0 | |
| ; CHECK-NEXT: .LBB3_1: # %loop | |
| ; CHECK-NEXT: # =>This Inner Loop Header: Depth=1 | |
| ; CHECK-NEXT: w6 = w5 | |
| ; CHECK-NEXT: w7 = *(u32 *)(r1 + 0) | |
| ; CHECK-NEXT: w5 = w7 | |
| ; CHECK-NEXT: w5 -= w6 | |
| ; CHECK-NEXT: w3 *= w7 | |
| ; CHECK-NEXT: w0 ^= w7 | |
| ; CHECK-NEXT: r1 += 4 | |
| ; CHECK-NEXT: r4 += 1 | |
| ; CHECK-NEXT: if r4 < r2 goto .LBB3_1 | |
| ; CHECK-NEXT: # %bb.2: # %exit | |
| ; CHECK-NEXT: w0 ^= w3 | |
| ; CHECK-NEXT: w0 ^= w5 | |
| ; CHECK-NEXT: exit | |
| entry: | |
| br label %loop | |
| loop: | |
| %i = phi i64 [ 0, %entry ], [ %i.next, %loop ] | |
| %xor = phi i32 [ 0, %entry ], [ %xor.next, %loop ] | |
| %mul = phi i32 [ 1, %entry ], [ %mul.next, %loop ] | |
| %sub = phi i32 [ 0, %entry ], [ %sub.next, %loop ] | |
| %a = getelementptr inbounds i32, ptr %p, i64 %i | |
| %v = load i32, ptr %a | |
| %xor.next = xor i32 %v, %xor | |
| %mul.next = mul i32 %v, %mul | |
| %sub.next = sub i32 %v, %sub | |
| %i.next = add i64 %i, 1 | |
| %c = icmp ult i64 %i.next, %n | |
| br i1 %c, label %loop, label %exit | |
| exit: | |
| %r0 = xor i32 %xor.next, %mul.next | |
| %r1 = xor i32 %r0, %sub.next | |
| ret i32 %r1 | |
| } |