Index: lib/Target/AMDGPU/SIInstrInfo.td =================================================================== --- lib/Target/AMDGPU/SIInstrInfo.td +++ lib/Target/AMDGPU/SIInstrInfo.td @@ -299,6 +299,19 @@ return Limit < 10; }]>; +def NegateImm : SDNodeXFormgetConstant(-N->getSExtValue(), SDLoc(N), MVT::i32); +}]>; + +// TODO: When FP inline imm values work? +def NegSubInlineConst32 : ImmLeaf= -64; +}], NegateImm>; + +def NegSubInlineConst16 : ImmLeaf= -64; +}], NegateImm>; + //===----------------------------------------------------------------------===// // Custom Operands //===----------------------------------------------------------------------===// Index: lib/Target/AMDGPU/SIInstructions.td =================================================================== --- lib/Target/AMDGPU/SIInstructions.td +++ lib/Target/AMDGPU/SIInstructions.td @@ -1078,6 +1078,15 @@ def : IntMed3Pat; def : IntMed3Pat; + +// Undo sub x, c -> add x, -c canonicalization since c is more likely +// an inline immediate than -c. +// TODO: Also do for 64-bit. +def : Pat< + (add i32:$src0, (i32 NegSubInlineConst32:$src1)), + (S_SUB_I32 $src0, NegSubInlineConst32:$src1) +>; + //============================================================================// // Assembler aliases //============================================================================// Index: lib/Target/AMDGPU/VOP2Instructions.td =================================================================== --- lib/Target/AMDGPU/VOP2Instructions.td +++ lib/Target/AMDGPU/VOP2Instructions.td @@ -453,6 +453,14 @@ (V_CNDMASK_B32_e64 (i32 0), (i32 -1), $src) >; +// Undo sub x, c -> add x, -c canonicalization since c is more likely +// an inline immediate than -c. +// TODO: Also do for 64-bit. +def : Pat< + (add i16:$src0, (i16 NegSubInlineConst16:$src1)), + (V_SUB_U16_e64 $src0, NegSubInlineConst16:$src1) +>; + } // End Predicates = [isVI] //===----------------------------------------------------------------------===// Index: test/CodeGen/AMDGPU/s_addk_i32.ll =================================================================== --- test/CodeGen/AMDGPU/s_addk_i32.ll +++ test/CodeGen/AMDGPU/s_addk_i32.ll @@ -37,7 +37,7 @@ } ; SI-LABEL: {{^}}s_addk_i32_k2: -; SI: s_addk_i32 {{s[0-9]+}}, 0xffef{{$}} +; SI: s_sub_i32 s{{[0-9]+}}, s{{[0-9]+}}, 17 ; SI: s_endpgm define void @s_addk_i32_k2(i32 addrspace(1)* %out, i32 %b) { %add = add i32 %b, -17 @@ -45,6 +45,15 @@ ret void } +; SI-LABEL: {{^}}s_addk_i32_k3: +; SI: s_addk_i32 {{s[0-9]+}}, 0xffbf{{$}} +; SI: s_endpgm +define void @s_addk_i32_k3(i32 addrspace(1)* %out, i32 %b) { + %add = add i32 %b, -65 + store i32 %add, i32 addrspace(1)* %out + ret void +} + ; SI-LABEL: {{^}}s_addk_v2i32_k0: ; SI-DAG: s_addk_i32 {{s[0-9]+}}, 0x41 ; SI-DAG: s_addk_i32 {{s[0-9]+}}, 0x42 Index: test/CodeGen/AMDGPU/shrink-add-sub-constant.ll =================================================================== --- /dev/null +++ test/CodeGen/AMDGPU/shrink-add-sub-constant.ll @@ -0,0 +1,183 @@ +; RUN: llc -march=amdgcn -verify-machineinstrs < %s | FileCheck -check-prefix=GCN -check-prefix=SI %s +; RUN: llc -march=amdgcn -mcpu=tonga -verify-machineinstrs < %s | FileCheck -check-prefix=GCN -check-prefix=VI %s + +; Test that add/sub with a constant is swapped to sub/add with negated +; constant to minimize code size. + +; GCN-LABEL: {{^}}v_test_i32_x_sub_64: +; GCN: {{buffer|flat}}_load_dword [[X:v[0-9]+]] +; GCN: v_subrev_i32_e32 v{{[0-9]+}}, vcc, 64, [[X]] +define void @v_test_i32_x_sub_64(i32 addrspace(1)* %out, i32 addrspace(1)* %in) #0 { + %tid = call i32 @llvm.amdgcn.workitem.id.x() + %tid.ext = sext i32 %tid to i64 + %gep = getelementptr inbounds i32, i32 addrspace(1)* %in, i64 %tid.ext + %gep.out = getelementptr inbounds i32, i32 addrspace(1)* %out, i64 %tid.ext + %x = load i32, i32 addrspace(1)* %gep + %result = sub i32 %x, 64 + store i32 %result, i32 addrspace(1)* %gep.out + ret void +} + +; GCN-LABEL: {{^}}v_test_i32_x_sub_64_multi_use: +; GCN: {{buffer|flat}}_load_dword [[X:v[0-9]+]] +; GCN: {{buffer|flat}}_load_dword [[Y:v[0-9]+]] +; GCN-DAG: v_subrev_i32_e32 v{{[0-9]+}}, vcc, 64, [[X]] +; GCN-DAG: v_subrev_i32_e32 v{{[0-9]+}}, vcc, 64, [[Y]] +define void @v_test_i32_x_sub_64_multi_use(i32 addrspace(1)* %out, i32 addrspace(1)* %in) #0 { + %tid = call i32 @llvm.amdgcn.workitem.id.x() + %tid.ext = sext i32 %tid to i64 + %gep = getelementptr inbounds i32, i32 addrspace(1)* %in, i64 %tid.ext + %gep.out = getelementptr inbounds i32, i32 addrspace(1)* %out, i64 %tid.ext + %x = load volatile i32, i32 addrspace(1)* %gep + %y = load volatile i32, i32 addrspace(1)* %gep + %result0 = sub i32 %x, 64 + %result1 = sub i32 %y, 64 + store volatile i32 %result0, i32 addrspace(1)* %gep.out + store volatile i32 %result1, i32 addrspace(1)* %gep.out + ret void +} + +; GCN-LABEL: {{^}}v_test_i32_64_sub_x: +; GCN: {{buffer|flat}}_load_dword [[X:v[0-9]+]] +; GCN: v_sub_i32_e32 v{{[0-9]+}}, vcc, 64, [[X]] +define void @v_test_i32_64_sub_x(i32 addrspace(1)* %out, i32 addrspace(1)* %in) #0 { + %tid = call i32 @llvm.amdgcn.workitem.id.x() + %tid.ext = sext i32 %tid to i64 + %gep = getelementptr inbounds i32, i32 addrspace(1)* %in, i64 %tid.ext + %gep.out = getelementptr inbounds i32, i32 addrspace(1)* %out, i64 %tid.ext + %x = load i32, i32 addrspace(1)* %gep + %result = sub i32 64, %x + store i32 %result, i32 addrspace(1)* %gep.out + ret void +} + +; GCN-LABEL: {{^}}v_test_i32_x_sub_65: +; GCN: {{buffer|flat}}_load_dword [[X:v[0-9]+]] +; GCN: v_add_i32_e32 v{{[0-9]+}}, vcc, 0xffffffbf, [[X]] +define void @v_test_i32_x_sub_65(i32 addrspace(1)* %out, i32 addrspace(1)* %in) #0 { + %tid = call i32 @llvm.amdgcn.workitem.id.x() + %tid.ext = sext i32 %tid to i64 + %gep = getelementptr inbounds i32, i32 addrspace(1)* %in, i64 %tid.ext + %gep.out = getelementptr inbounds i32, i32 addrspace(1)* %out, i64 %tid.ext + %x = load i32, i32 addrspace(1)* %gep + %result = sub i32 %x, 65 + store i32 %result, i32 addrspace(1)* %gep.out + ret void +} + +; GCN-LABEL: {{^}}v_test_i32_65_sub_x: +; GCN: {{buffer|flat}}_load_dword [[X:v[0-9]+]] +; GCN: v_sub_i32_e32 v{{[0-9]+}}, vcc, 0x41, [[X]] +define void @v_test_i32_65_sub_x(i32 addrspace(1)* %out, i32 addrspace(1)* %in) #0 { + %tid = call i32 @llvm.amdgcn.workitem.id.x() + %tid.ext = sext i32 %tid to i64 + %gep = getelementptr inbounds i32, i32 addrspace(1)* %in, i64 %tid.ext + %gep.out = getelementptr inbounds i32, i32 addrspace(1)* %out, i64 %tid.ext + %x = load i32, i32 addrspace(1)* %gep + %result = sub i32 65, %x + store i32 %result, i32 addrspace(1)* %gep.out + ret void +} + +; GCN-LABEL: {{^}}v_test_i32_x_sub_neg16: +; GCN: {{buffer|flat}}_load_dword [[X:v[0-9]+]] +; GCN: v_add_i32_e32 v{{[0-9]+}}, vcc, 16, [[X]] +define void @v_test_i32_x_sub_neg16(i32 addrspace(1)* %out, i32 addrspace(1)* %in) #0 { + %tid = call i32 @llvm.amdgcn.workitem.id.x() + %tid.ext = sext i32 %tid to i64 + %gep = getelementptr inbounds i32, i32 addrspace(1)* %in, i64 %tid.ext + %gep.out = getelementptr inbounds i32, i32 addrspace(1)* %out, i64 %tid.ext + %x = load i32, i32 addrspace(1)* %gep + %result = sub i32 %x, -16 + store i32 %result, i32 addrspace(1)* %gep.out + ret void +} + +; GCN-LABEL: {{^}}v_test_i32_neg16_sub_x: +; GCN: {{buffer|flat}}_load_dword [[X:v[0-9]+]] +; GCN: v_sub_i32_e32 v{{[0-9]+}}, vcc, -16, [[X]] +define void @v_test_i32_neg16_sub_x(i32 addrspace(1)* %out, i32 addrspace(1)* %in) #0 { + %tid = call i32 @llvm.amdgcn.workitem.id.x() + %tid.ext = sext i32 %tid to i64 + %gep = getelementptr inbounds i32, i32 addrspace(1)* %in, i64 %tid.ext + %gep.out = getelementptr inbounds i32, i32 addrspace(1)* %out, i64 %tid.ext + %x = load i32, i32 addrspace(1)* %gep + %result = sub i32 -16, %x + store i32 %result, i32 addrspace(1)* %gep.out + ret void +} + +; GCN-LABEL: {{^}}v_test_i32_x_sub_neg17: +; GCN: {{buffer|flat}}_load_dword [[X:v[0-9]+]] +; GCN: v_add_i32_e32 v{{[0-9]+}}, vcc, 17, [[X]] +define void @v_test_i32_x_sub_neg17(i32 addrspace(1)* %out, i32 addrspace(1)* %in) #0 { + %tid = call i32 @llvm.amdgcn.workitem.id.x() + %tid.ext = sext i32 %tid to i64 + %gep = getelementptr inbounds i32, i32 addrspace(1)* %in, i64 %tid.ext + %gep.out = getelementptr inbounds i32, i32 addrspace(1)* %out, i64 %tid.ext + %x = load i32, i32 addrspace(1)* %gep + %result = sub i32 %x, -17 + store i32 %result, i32 addrspace(1)* %gep.out + ret void +} + +; GCN-LABEL: {{^}}v_test_i32_neg17_sub_x: +; GCN: {{buffer|flat}}_load_dword [[X:v[0-9]+]] +; GCN: v_sub_i32_e32 v{{[0-9]+}}, vcc, 0xffffffef, [[X]] +define void @v_test_i32_neg17_sub_x(i32 addrspace(1)* %out, i32 addrspace(1)* %in) #0 { + %tid = call i32 @llvm.amdgcn.workitem.id.x() + %tid.ext = sext i32 %tid to i64 + %gep = getelementptr inbounds i32, i32 addrspace(1)* %in, i64 %tid.ext + %gep.out = getelementptr inbounds i32, i32 addrspace(1)* %out, i64 %tid.ext + %x = load i32, i32 addrspace(1)* %gep + %result = sub i32 -17, %x + store i32 %result, i32 addrspace(1)* %gep.out + ret void +} + +; GCN-LABEL: {{^}}s_test_i32_x_sub_64: +; GCN: s_load_dword [[X:s[0-9]+]] +; GCN: s_sub_i32 s{{[0-9]+}}, [[X]], 64 +define void @s_test_i32_x_sub_64(i32 %x) #0 { + %result = sub i32 %x, 64 + call void asm sideeffect "; use $0", "s"(i32 %result) + ret void +} + +; GCN-LABEL: {{^}}v_test_i16_x_sub_64: +; VI: {{buffer|flat}}_load_ushort [[X:v[0-9]+]] +; VI: v_subrev_u16_e32 v{{[0-9]+}}, 64, [[X]] +define void @v_test_i16_x_sub_64(i16 addrspace(1)* %out, i16 addrspace(1)* %in) #0 { + %tid = call i32 @llvm.amdgcn.workitem.id.x() + %tid.ext = sext i32 %tid to i64 + %gep = getelementptr inbounds i16, i16 addrspace(1)* %in, i64 %tid.ext + %gep.out = getelementptr inbounds i16, i16 addrspace(1)* %out, i64 %tid.ext + %x = load i16, i16 addrspace(1)* %gep + %result = sub i16 %x, 64 + store i16 %result, i16 addrspace(1)* %gep.out + ret void +} + +; GCN-LABEL: {{^}}v_test_i16_x_sub_64_multi_use: +; GCN: {{buffer|flat}}_load_ushort [[X:v[0-9]+]] +; GCN: {{buffer|flat}}_load_ushort [[Y:v[0-9]+]] +; GCN-DAG: v_subrev_u16_e32 v{{[0-9]+}}, 64, [[X]] +; GCN-DAG: v_subrev_u16_e32 v{{[0-9]+}}, 64, [[Y]] +define void @v_test_i16_x_sub_64_multi_use(i16 addrspace(1)* %out, i16 addrspace(1)* %in) #0 { + %tid = call i32 @llvm.amdgcn.workitem.id.x() + %tid.ext = sext i32 %tid to i64 + %gep = getelementptr inbounds i16, i16 addrspace(1)* %in, i64 %tid.ext + %gep.out = getelementptr inbounds i16, i16 addrspace(1)* %out, i64 %tid.ext + %x = load volatile i16, i16 addrspace(1)* %gep + %y = load volatile i16, i16 addrspace(1)* %gep + %result0 = sub i16 %x, 64 + %result1 = sub i16 %y, 64 + store volatile i16 %result0, i16 addrspace(1)* %gep.out + store volatile i16 %result1, i16 addrspace(1)* %gep.out + ret void +} + +declare i32 @llvm.amdgcn.workitem.id.x() #1 + +attributes #0 = { nounwind } +attributes #1 = { nounwind readnone }