legalForTypesWithMemDesc
Marks (op, type) pairs as legal when they match a full memory description tuple:
{result type, address type, stored type, alignment}
Rule
// Legal: load/store s16 from addrspace0, 8-bit alignment
getActionDefinitionsBuilder(G_LOAD)
.legalForTypesWithMemDesc({{LLT::scalar(16), LLT::pointer(0, PtrBits), LLT::scalar(16), 8}});
getActionDefinitionsBuilder(G_STORE)
.legalForTypesWithMemDesc({{LLT::scalar(16), LLT::pointer(0, PtrBits), LLT::scalar(16), 8}});
// Legal: extending load / truncating store (s16 result, s8 memory)
getActionDefinitionsBuilder(G_LOAD)
.legalForTypesWithMemDesc({{LLT::scalar(16), LLT::pointer(0, PtrBits), LLT::scalar(8), 8}});
getActionDefinitionsBuilder(G_STORE)
.legalForTypesWithMemDesc({{LLT::scalar(16), LLT::pointer(0, PtrBits), LLT::scalar(8), 8}});Effect
{s16, p0, s16, 8}= load/store a 16-bit scalar in addrspace 0, byte-aligned.{s16, p0, s8, 8}= extending load or truncating store.
Before
%p = COPY %addr
%v16 = G_LOAD s16, %p :: (load 1 from %p, addrspace 0, stored s8, align 8)
After
- Unchanged, because it’s marked legal.
lowerIf
Applies the lowering action if a predicate evaluates true. Replaces the op with a simpler sequence.
Rule
auto IsIntGT32 = [](const LegalityQuery &Q){
return Q.Types[0].isScalar() && Q.Types[0].getScalarSizeInBits() > 32;
};
getActionDefinitionsBuilder(G_UREM)
.lowerIf(IsIntGT32);Before
%r = G_UREM s64 %x, %y
After
%q = G_UDIV s64 %x, %y
%t = G_MUL s64 %q, %y
%r = G_SUB s64 %x, %t
legalIf
Marks an operation as legal only under a condition (no rewrite when true).
Rule
auto IsPtr32 = [](const LegalityQuery &Q){
return Q.Types[1].isPointer() && Q.Types[1].getSizeInBits() == 32;
};
getActionDefinitionsBuilder(G_LOAD)
.legalIf(IsPtr32) // load is legal only with 32-bit pointers
.lower(); // otherwise lowerBefore (legal)
%r = G_LOAD s64, %p // with %p: p0 (32-bit ptr)
After (legal)
%r = G_LOAD s64, %p // unchanged
Before (illegal)
%r = G_LOAD s64, %p // with %p: p1 (64-bit ptr)
After (lowered)
%r_lo = G_LOAD s32, %p
%r_hi = G_LOAD s32, %p+4
%r = G_MERGE_VALUES %r_lo, %r_hi
scalarize
Converts a vector op into multiple scalar ops on its elements.
Rule
getActionDefinitionsBuilder(G_ADD)
.legalFor({ LLT::fixed_vector(2, 32) })
.scalarize(0);Before
%v = G_ADD <4 x s32> %a, %b
After
%a0, %a1, %a2, %a3 = G_UNMERGE_VALUES %a
%b0, %b1, %b2, %b3 = G_UNMERGE_VALUES %b
%s0 = G_ADD s32 %a0, %b0
%s1 = G_ADD s32 %a1, %b1
%s2 = G_ADD s32 %a2, %b2
%s3 = G_ADD s32 %a3, %b3
%v = G_BUILD_VECTOR %s0, %s1, %s2, %s3
lower
Always lowers the instruction (no predicate). The replacement depends on the opcode.
Rule
getActionDefinitionsBuilder(G_FNEG)
.lower();Before
%r = G_FNEG s32 %x
After
%negzero = G_FCONSTANT float -0.0
%r = G_FSUB s32 %negzero, %x
clampScalar
Constrains the legal scalar type at a given index to a range. Below min → widen. Above max → narrow.
Rule
getActionDefinitionsBuilder(G_ADD)
.clampScalar(0, LLT::scalar(16), LLT::scalar(32));Case A (widen)
- Before:
%r = G_ADD s8 %x, %y - After:
%xw = G_ANYEXT s16 %x
%yw = G_ANYEXT s16 %y
%rw = G_ADD s16 %xw, %yw
%r = G_TRUNC s8 %rw
Case B (narrow)
- Before:
%r = G_ADD s64 %x, %y - After:
%x_lo, %x_hi = G_UNMERGE_VALUES %x
%y_lo, %y_hi = G_UNMERGE_VALUES %y
%lo = G_ADD s32 %x_lo, %y_lo
%hi = G_ADDE s32 %x_hi, %y_hi, /*carry_in*/
%r = G_MERGE_VALUES %lo, %hi