Skip to content

Commit 8269fd2

Browse files
committed
[GlobalIsel][X86] Add initial scalar G_MUL/G_SMULH/G_UMULH instruction selection handling
Reuse the existing div/rem selection code to also handle mul/imul to support G_MUL/G_SMULH/G_UMULH, as they have a similar pattern using rDX/rAX for mulh/mul results, plus the AH/AL support for i8 multiplies.
1 parent 878e498 commit 8269fd2

File tree

4 files changed

+138
-53
lines changed

4 files changed

+138
-53
lines changed

llvm/lib/Target/X86/X86InstructionSelector.cpp

Lines changed: 65 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ class X86InstructionSelector : public InstructionSelector {
114114
bool materializeFP(MachineInstr &I, MachineRegisterInfo &MRI,
115115
MachineFunction &MF) const;
116116
bool selectImplicitDefOrPHI(MachineInstr &I, MachineRegisterInfo &MRI) const;
117-
bool selectDivRem(MachineInstr &I, MachineRegisterInfo &MRI,
118-
MachineFunction &MF) const;
117+
bool selectMulDivRem(MachineInstr &I, MachineRegisterInfo &MRI,
118+
MachineFunction &MF) const;
119119
bool selectIntrinsicWSideEffects(MachineInstr &I, MachineRegisterInfo &MRI,
120120
MachineFunction &MF) const;
121121

@@ -421,11 +421,14 @@ bool X86InstructionSelector::select(MachineInstr &I) {
421421
case TargetOpcode::G_IMPLICIT_DEF:
422422
case TargetOpcode::G_PHI:
423423
return selectImplicitDefOrPHI(I, MRI);
424+
case TargetOpcode::G_MUL:
425+
case TargetOpcode::G_SMULH:
426+
case TargetOpcode::G_UMULH:
424427
case TargetOpcode::G_SDIV:
425428
case TargetOpcode::G_UDIV:
426429
case TargetOpcode::G_SREM:
427430
case TargetOpcode::G_UREM:
428-
return selectDivRem(I, MRI, MF);
431+
return selectMulDivRem(I, MRI, MF);
429432
case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS:
430433
return selectIntrinsicWSideEffects(I, MRI, MF);
431434
}
@@ -1558,11 +1561,14 @@ bool X86InstructionSelector::selectImplicitDefOrPHI(
15581561
return true;
15591562
}
15601563

1561-
bool X86InstructionSelector::selectDivRem(MachineInstr &I,
1562-
MachineRegisterInfo &MRI,
1563-
MachineFunction &MF) const {
1564-
// The implementation of this function is taken from X86FastISel.
1565-
assert((I.getOpcode() == TargetOpcode::G_SDIV ||
1564+
bool X86InstructionSelector::selectMulDivRem(MachineInstr &I,
1565+
MachineRegisterInfo &MRI,
1566+
MachineFunction &MF) const {
1567+
// The implementation of this function is adapted from X86FastISel.
1568+
assert((I.getOpcode() == TargetOpcode::G_MUL ||
1569+
I.getOpcode() == TargetOpcode::G_SMULH ||
1570+
I.getOpcode() == TargetOpcode::G_UMULH ||
1571+
I.getOpcode() == TargetOpcode::G_SDIV ||
15661572
I.getOpcode() == TargetOpcode::G_SREM ||
15671573
I.getOpcode() == TargetOpcode::G_UDIV ||
15681574
I.getOpcode() == TargetOpcode::G_UREM) &&
@@ -1581,10 +1587,11 @@ bool X86InstructionSelector::selectDivRem(MachineInstr &I,
15811587
return false;
15821588

15831589
const static unsigned NumTypes = 4; // i8, i16, i32, i64
1584-
const static unsigned NumOps = 4; // SDiv, SRem, UDiv, URem
1590+
const static unsigned NumOps = 7; // SDiv/SRem/UDiv/URem/Mul/SMulH/UMulh
15851591
const static bool S = true; // IsSigned
15861592
const static bool U = false; // !IsSigned
15871593
const static unsigned Copy = TargetOpcode::COPY;
1594+
15881595
// For the X86 IDIV instruction, in most cases the dividend
15891596
// (numerator) must be in a specific register pair highreg:lowreg,
15901597
// producing the quotient in lowreg and the remainder in highreg.
@@ -1593,19 +1600,19 @@ bool X86InstructionSelector::selectDivRem(MachineInstr &I,
15931600
// exception is i8, where the dividend is defined as a single register rather
15941601
// than a register pair, and we therefore directly sign-extend the dividend
15951602
// into lowreg, instead of copying, and ignore the highreg.
1596-
const static struct DivRemEntry {
1603+
const static struct MulDivRemEntry {
15971604
// The following portion depends only on the data type.
15981605
unsigned SizeInBits;
15991606
unsigned LowInReg; // low part of the register pair
16001607
unsigned HighInReg; // high part of the register pair
16011608
// The following portion depends on both the data type and the operation.
1602-
struct DivRemResult {
1603-
unsigned OpDivRem; // The specific DIV/IDIV opcode to use.
1609+
struct MulDivRemResult {
1610+
unsigned OpMulDivRem; // The specific MUL/DIV opcode to use.
16041611
unsigned OpSignExtend; // Opcode for sign-extending lowreg into
16051612
// highreg, or copying a zero into highreg.
16061613
unsigned OpCopy; // Opcode for copying dividend into lowreg, or
16071614
// zero/sign-extending into lowreg for i8.
1608-
unsigned DivRemResultReg; // Register containing the desired result.
1615+
unsigned ResultReg; // Register containing the desired result.
16091616
bool IsOpSigned; // Whether to use signed or unsigned form.
16101617
} ResultTable[NumOps];
16111618
} OpTable[NumTypes] = {
@@ -1617,25 +1624,34 @@ bool X86InstructionSelector::selectDivRem(MachineInstr &I,
16171624
{X86::IDIV8r, 0, X86::MOVSX16rr8, X86::AH, S}, // SRem
16181625
{X86::DIV8r, 0, X86::MOVZX16rr8, X86::AL, U}, // UDiv
16191626
{X86::DIV8r, 0, X86::MOVZX16rr8, X86::AH, U}, // URem
1627+
{X86::IMUL8r, 0, X86::MOVSX16rr8, X86::AL, S}, // Mul
1628+
{X86::IMUL8r, 0, X86::MOVSX16rr8, X86::AH, S}, // SMulH
1629+
{X86::MUL8r, 0, X86::MOVZX16rr8, X86::AH, U}, // UMulH
16201630
}}, // i8
16211631
{16,
16221632
X86::AX,
16231633
X86::DX,
16241634
{
1625-
{X86::IDIV16r, X86::CWD, Copy, X86::AX, S}, // SDiv
1626-
{X86::IDIV16r, X86::CWD, Copy, X86::DX, S}, // SRem
1627-
{X86::DIV16r, X86::MOV32r0, Copy, X86::AX, U}, // UDiv
1628-
{X86::DIV16r, X86::MOV32r0, Copy, X86::DX, U}, // URem
1629-
}}, // i16
1635+
{X86::IDIV16r, X86::CWD, Copy, X86::AX, S}, // SDiv
1636+
{X86::IDIV16r, X86::CWD, Copy, X86::DX, S}, // SRem
1637+
{X86::DIV16r, X86::MOV32r0, Copy, X86::AX, U}, // UDiv
1638+
{X86::DIV16r, X86::MOV32r0, Copy, X86::DX, U}, // URem
1639+
{X86::IMUL16r, X86::MOV32r0, Copy, X86::AX, S}, // Mul
1640+
{X86::IMUL16r, X86::MOV32r0, Copy, X86::DX, S}, // SMulH
1641+
{X86::MUL16r, X86::MOV32r0, Copy, X86::DX, U}, // UMulH
1642+
}}, // i16
16301643
{32,
16311644
X86::EAX,
16321645
X86::EDX,
16331646
{
1634-
{X86::IDIV32r, X86::CDQ, Copy, X86::EAX, S}, // SDiv
1635-
{X86::IDIV32r, X86::CDQ, Copy, X86::EDX, S}, // SRem
1636-
{X86::DIV32r, X86::MOV32r0, Copy, X86::EAX, U}, // UDiv
1637-
{X86::DIV32r, X86::MOV32r0, Copy, X86::EDX, U}, // URem
1638-
}}, // i32
1647+
{X86::IDIV32r, X86::CDQ, Copy, X86::EAX, S}, // SDiv
1648+
{X86::IDIV32r, X86::CDQ, Copy, X86::EDX, S}, // SRem
1649+
{X86::DIV32r, X86::MOV32r0, Copy, X86::EAX, U}, // UDiv
1650+
{X86::DIV32r, X86::MOV32r0, Copy, X86::EDX, U}, // URem
1651+
{X86::IMUL32r, X86::MOV32r0, Copy, X86::EAX, S}, // Mul
1652+
{X86::IMUL32r, X86::MOV32r0, Copy, X86::EDX, S}, // SMulH
1653+
{X86::MUL32r, X86::MOV32r0, Copy, X86::EDX, U}, // UMulH
1654+
}}, // i32
16391655
{64,
16401656
X86::RAX,
16411657
X86::RDX,
@@ -1644,10 +1660,13 @@ bool X86InstructionSelector::selectDivRem(MachineInstr &I,
16441660
{X86::IDIV64r, X86::CQO, Copy, X86::RDX, S}, // SRem
16451661
{X86::DIV64r, X86::MOV32r0, Copy, X86::RAX, U}, // UDiv
16461662
{X86::DIV64r, X86::MOV32r0, Copy, X86::RDX, U}, // URem
1647-
}}, // i64
1663+
{X86::IMUL64r, X86::MOV32r0, Copy, X86::RAX, S}, // Mul
1664+
{X86::IMUL64r, X86::MOV32r0, Copy, X86::RDX, S}, // SMulH
1665+
{X86::MUL64r, X86::MOV32r0, Copy, X86::RDX, U}, // UMulH
1666+
}}, // i64
16481667
};
16491668

1650-
auto OpEntryIt = llvm::find_if(OpTable, [RegTy](const DivRemEntry &El) {
1669+
auto OpEntryIt = llvm::find_if(OpTable, [RegTy](const MulDivRemEntry &El) {
16511670
return El.SizeInBits == RegTy.getSizeInBits();
16521671
});
16531672
if (OpEntryIt == std::end(OpTable))
@@ -1656,7 +1675,7 @@ bool X86InstructionSelector::selectDivRem(MachineInstr &I,
16561675
unsigned OpIndex;
16571676
switch (I.getOpcode()) {
16581677
default:
1659-
llvm_unreachable("Unexpected div/rem opcode");
1678+
llvm_unreachable("Unexpected mul/div/rem opcode");
16601679
case TargetOpcode::G_SDIV:
16611680
OpIndex = 0;
16621681
break;
@@ -1669,10 +1688,20 @@ bool X86InstructionSelector::selectDivRem(MachineInstr &I,
16691688
case TargetOpcode::G_UREM:
16701689
OpIndex = 3;
16711690
break;
1691+
case TargetOpcode::G_MUL:
1692+
OpIndex = 4;
1693+
break;
1694+
case TargetOpcode::G_SMULH:
1695+
OpIndex = 5;
1696+
break;
1697+
case TargetOpcode::G_UMULH:
1698+
OpIndex = 6;
1699+
break;
16721700
}
16731701

1674-
const DivRemEntry &TypeEntry = *OpEntryIt;
1675-
const DivRemEntry::DivRemResult &OpEntry = TypeEntry.ResultTable[OpIndex];
1702+
const MulDivRemEntry &TypeEntry = *OpEntryIt;
1703+
const MulDivRemEntry::MulDivRemResult &OpEntry =
1704+
TypeEntry.ResultTable[OpIndex];
16761705

16771706
const TargetRegisterClass *RegRC = getRegClass(RegTy, *RegRB);
16781707
if (!RBI.constrainGenericRegister(Op1Reg, *RegRC, MRI) ||
@@ -1687,6 +1716,7 @@ bool X86InstructionSelector::selectDivRem(MachineInstr &I,
16871716
BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(OpEntry.OpCopy),
16881717
TypeEntry.LowInReg)
16891718
.addReg(Op1Reg);
1719+
16901720
// Zero-extend or sign-extend into high-order input register.
16911721
if (OpEntry.OpSignExtend) {
16921722
if (OpEntry.IsOpSigned)
@@ -1717,9 +1747,11 @@ bool X86InstructionSelector::selectDivRem(MachineInstr &I,
17171747
}
17181748
}
17191749
}
1720-
// Generate the DIV/IDIV instruction.
1721-
BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(OpEntry.OpDivRem))
1750+
1751+
// Generate the DIV/IDIV/MUL/IMUL instruction.
1752+
BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(OpEntry.OpMulDivRem))
17221753
.addReg(Op2Reg);
1754+
17231755
// For i8 remainder, we can't reference ah directly, as we'll end
17241756
// up with bogus copies like %r9b = COPY %ah. Reference ax
17251757
// instead to prevent ah references in a rex instruction.
@@ -1728,7 +1760,7 @@ bool X86InstructionSelector::selectDivRem(MachineInstr &I,
17281760
// won't generate explicit references to the GR8_NOREX registers. If
17291761
// the allocator and/or the backend get enhanced to be more robust in
17301762
// that regard, this can be, and should be, removed.
1731-
if (OpEntry.DivRemResultReg == X86::AH && STI.is64Bit()) {
1763+
if (OpEntry.ResultReg == X86::AH && STI.is64Bit()) {
17321764
Register SourceSuperReg = MRI.createVirtualRegister(&X86::GR16RegClass);
17331765
Register ResultSuperReg = MRI.createVirtualRegister(&X86::GR16RegClass);
17341766
BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Copy), SourceSuperReg)
@@ -1750,9 +1782,10 @@ bool X86InstructionSelector::selectDivRem(MachineInstr &I,
17501782
} else {
17511783
BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(TargetOpcode::COPY),
17521784
DstReg)
1753-
.addReg(OpEntry.DivRemResultReg);
1785+
.addReg(OpEntry.ResultReg);
17541786
}
17551787
I.eraseFromParent();
1788+
17561789
return true;
17571790
}
17581791

llvm/lib/Target/X86/X86LegalizerInfo.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,15 @@ X86LegalizerInfo::X86LegalizerInfo(const X86Subtarget &STI,
196196
.clampScalar(0, s8, sMaxScalar)
197197
.scalarize(0);
198198

199+
getActionDefinitionsBuilder({G_SMULH, G_UMULH})
200+
.legalIf([=](const LegalityQuery &Query) -> bool {
201+
return typeInSet(0, {s8, s16, s32})(Query) ||
202+
(Is64Bit && typeInSet(0, {s64})(Query));
203+
})
204+
.widenScalarToNextPow2(0, /*Min=*/32)
205+
.clampScalar(0, s8, sMaxScalar)
206+
.scalarize(0);
207+
199208
// integer divisions
200209
getActionDefinitionsBuilder({G_SDIV, G_SREM, G_UDIV, G_UREM})
201210
.legalIf([=](const LegalityQuery &Query) -> bool {

llvm/test/CodeGen/X86/GlobalISel/legalize-mul-scalar.mir

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
22
# RUN: llc -O0 -mtriple=x86_64-linux-gnu -run-pass=legalizer %s -o - | FileCheck %s --check-prefixes=CHECK,X64
3-
# RUN: llc -O0 -mtriple=i386-linux-gnu -run-pass=legalizer -global-isel-abort=2 -pass-remarks-missed='gisel*' %s 2>%t -o - | FileCheck %s --check-prefixes=CHECK,X86
4-
# RUN: FileCheck -check-prefix=ERR32 %s < %t
5-
6-
# ERR32: remark: <unknown>:0:0: unable to legalize instruction: %14:_(s32) = G_UMULH %7:_, %9:_ (in function: test_mul_i42)
7-
# ERR32: remark: <unknown>:0:0: unable to legalize instruction: %10:_(s32) = G_UMULH %3:_, %5:_ (in function: test_mul_i64)
3+
# RUN: llc -O0 -mtriple=i386-linux-gnu -run-pass=legalizer %s -o - | FileCheck %s --check-prefixes=CHECK,X86
84

95
--- |
106
define void @test_mul_i1() { ret void }
@@ -200,21 +196,16 @@ body: |
200196
; X86: liveins: $rdi, $rsi
201197
; X86-NEXT: {{ $}}
202198
; X86-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $rdx
203-
; X86-NEXT: [[TRUNC:%[0-9]+]]:_(s42) = G_TRUNC [[COPY]](s64)
204-
; X86-NEXT: [[ANYEXT:%[0-9]+]]:_(s64) = G_ANYEXT [[TRUNC]](s42)
205-
; X86-NEXT: [[ANYEXT1:%[0-9]+]]:_(s64) = G_ANYEXT [[TRUNC]](s42)
206-
; X86-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[ANYEXT]](s64)
207-
; X86-NEXT: [[UV2:%[0-9]+]]:_(s32), [[UV3:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[ANYEXT1]](s64)
199+
; X86-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[COPY]](s64)
200+
; X86-NEXT: [[UV2:%[0-9]+]]:_(s32), [[UV3:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[COPY]](s64)
208201
; X86-NEXT: [[MUL:%[0-9]+]]:_(s32) = G_MUL [[UV]], [[UV2]]
209202
; X86-NEXT: [[MUL1:%[0-9]+]]:_(s32) = G_MUL [[UV1]], [[UV2]]
210203
; X86-NEXT: [[MUL2:%[0-9]+]]:_(s32) = G_MUL [[UV]], [[UV3]]
211204
; X86-NEXT: [[UMULH:%[0-9]+]]:_(s32) = G_UMULH [[UV]], [[UV2]]
212205
; X86-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[MUL1]], [[MUL2]]
213206
; X86-NEXT: [[ADD1:%[0-9]+]]:_(s32) = G_ADD [[ADD]], [[UMULH]]
214207
; X86-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[MUL]](s32), [[ADD1]](s32)
215-
; X86-NEXT: [[TRUNC1:%[0-9]+]]:_(s42) = G_TRUNC [[MV]](s64)
216-
; X86-NEXT: [[ANYEXT2:%[0-9]+]]:_(s64) = G_ANYEXT [[TRUNC1]](s42)
217-
; X86-NEXT: $rax = COPY [[ANYEXT2]](s64)
208+
; X86-NEXT: $rax = COPY [[MV]](s64)
218209
; X86-NEXT: RET 0
219210
%0(s64) = COPY $rdx
220211
%1(s42) = G_TRUNC %0(s64)
Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,92 @@
11
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
22
; RUN: llc -mtriple=x86_64-linux-gnu -global-isel -verify-machineinstrs < %s -o - | FileCheck %s --check-prefix=X64
3+
; RUN: llc -mtriple=i686-linux-gnu -global-isel -verify-machineinstrs < %s -o - | FileCheck %s --check-prefix=X86
34

4-
;TODO: instruction selection not supported yet
5-
;define i8 @test_mul_i8(i8 %arg1, i8 %arg2) {
6-
; %ret = mul i8 %arg1, %arg2
7-
; ret i8 %ret
8-
;}
5+
define i8 @test_mul_i8(i8 %arg1, i8 %arg2) nounwind {
6+
; X64-LABEL: test_mul_i8:
7+
; X64: # %bb.0:
8+
; X64-NEXT: movsbl %dil, %eax
9+
; X64-NEXT: imulb %sil
10+
; X64-NEXT: retq
11+
;
12+
; X86-LABEL: test_mul_i8:
13+
; X86: # %bb.0:
14+
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
15+
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
16+
; X86-NEXT: cbtw
17+
; X86-NEXT: imulb %cl
18+
; X86-NEXT: retl
19+
%ret = mul i8 %arg1, %arg2
20+
ret i8 %ret
21+
}
922

10-
define i16 @test_mul_i16(i16 %arg1, i16 %arg2) {
23+
define i16 @test_mul_i16(i16 %arg1, i16 %arg2) nounwind {
1124
; X64-LABEL: test_mul_i16:
1225
; X64: # %bb.0:
1326
; X64-NEXT: movl %esi, %eax
1427
; X64-NEXT: imulw %di, %ax
1528
; X64-NEXT: # kill: def $ax killed $ax killed $eax
1629
; X64-NEXT: retq
30+
;
31+
; X86-LABEL: test_mul_i16:
32+
; X86: # %bb.0:
33+
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
34+
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
35+
; X86-NEXT: imulw %cx, %ax
36+
; X86-NEXT: # kill: def $ax killed $ax killed $eax
37+
; X86-NEXT: retl
1738
%ret = mul i16 %arg1, %arg2
1839
ret i16 %ret
1940
}
2041

21-
define i32 @test_mul_i32(i32 %arg1, i32 %arg2) {
42+
define i32 @test_mul_i32(i32 %arg1, i32 %arg2) nounwind {
2243
; X64-LABEL: test_mul_i32:
2344
; X64: # %bb.0:
2445
; X64-NEXT: movl %esi, %eax
2546
; X64-NEXT: imull %edi, %eax
2647
; X64-NEXT: retq
48+
;
49+
; X86-LABEL: test_mul_i32:
50+
; X86: # %bb.0:
51+
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
52+
; X86-NEXT: imull {{[0-9]+}}(%esp), %eax
53+
; X86-NEXT: retl
2754
%ret = mul i32 %arg1, %arg2
2855
ret i32 %ret
2956
}
3057

31-
define i64 @test_mul_i64(i64 %arg1, i64 %arg2) {
58+
define i64 @test_mul_i64(i64 %arg1, i64 %arg2) nounwind {
3259
; X64-LABEL: test_mul_i64:
3360
; X64: # %bb.0:
3461
; X64-NEXT: movq %rsi, %rax
3562
; X64-NEXT: imulq %rdi, %rax
3663
; X64-NEXT: retq
64+
;
65+
; X86-LABEL: test_mul_i64:
66+
; X86: # %bb.0:
67+
; X86-NEXT: pushl %edi
68+
; X86-NEXT: pushl %esi
69+
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
70+
; X86-NEXT: movl {{[0-9]+}}(%esp), %edx
71+
; X86-NEXT: movl {{[0-9]+}}(%esp), %esi
72+
; X86-NEXT: imull %eax, %esi
73+
; X86-NEXT: movl %eax, %ecx
74+
; X86-NEXT: imull %edx, %ecx
75+
; X86-NEXT: movl {{[0-9]+}}(%esp), %edi
76+
; X86-NEXT: imull %edx, %edi
77+
; X86-NEXT: mull %edx
78+
; X86-NEXT: addl %edi, %esi
79+
; X86-NEXT: addl %esi, %edx
80+
; X86-NEXT: movl %ecx, %eax
81+
; X86-NEXT: popl %esi
82+
; X86-NEXT: popl %edi
83+
; X86-NEXT: retl
3784
%ret = mul i64 %arg1, %arg2
3885
ret i64 %ret
3986
}
4087

88+
;TODO: instruction selection not supported yet
89+
;define i128 @test_mul_i128(i128 %arg1, i128 %arg2) nounwind {
90+
; %ret = mul i128 %arg1, %arg2
91+
; ret i128 %ret
92+
;}

0 commit comments

Comments
 (0)