///|
fn lower_signedness(
signedness : @milkir.VectorSignedness,
) -> @native_types.Signedness {
match signedness {
Signed => Signed
Unsigned => Unsigned
}
}
///|
fn lower_vector_half(half : @milkir.VectorHalf) -> @lowering.VectorHalf {
match half {
Low => Low
High => High
}
}
///|
fn lower_integer_lane(lane : @milkir.VectorIntLane) -> @lowering.VectorLane {
match lane {
I8 => I8x16
I16 => I16x8
I32 => I32x4
I64 => I64x2
}
}
///|
fn lower_float_lane(lane : @milkir.VectorFloatLane) -> @lowering.VectorLane {
match lane {
F32 => F32x4
F64 => F64x2
}
}
///|
fn lower_lane(lane : @milkir.VectorLane) -> @lowering.VectorLane {
match lane {
I8 => I8x16
I16 => I16x8
I32 => I32x4
I64 => I64x2
F32 => F32x4
F64 => F64x2
}
}
///|
fn lower_extension(
extension : @milkir.VectorExtension,
) -> @native_types.Signedness? {
match extension {
None => None
Signed => Some(Signed)
Unsigned => Some(Unsigned)
}
}
///|
fn bytes_word(bytes : Bytes, offset : Int32) -> UInt64 {
let mut word = 0UL
for index in 0..<8 {
word = word | (bytes[offset + index].to_int().to_uint64() << (index * 8))
}
word
}
///|
fn lower_int_unary_vector(
operation : @milkir.VectorIntUnaryOp,
lane : @milkir.VectorIntLane,
) -> @lowering.VectorOp {
let lane = lower_integer_lane(lane)
match operation {
Abs => IntUnary(lane, Absolute)
Neg => IntUnary(lane, Negate)
Popcnt => IntUnary(lane, PopulationCount)
Extend(half, signedness) =>
Convert(
match half {
Low => ExtendLow(lane, lower_signedness(signedness))
High => ExtendHigh(lane, lower_signedness(signedness))
},
)
ExtAddPairwise(signedness) =>
IntUnary(lane, ExtendAddPairwise(lower_signedness(signedness)))
}
}
///|
fn lower_int_binary_vector(
operation : @milkir.VectorIntBinaryOp,
lane : @milkir.VectorIntLane,
) -> @lowering.VectorOp {
let lane = lower_integer_lane(lane)
let operation : @lowering.VectorIntBinaryOp = match operation {
Add => Add
Sub => Sub
Mul => Mul
AddSaturating(signedness) => SaturatingAdd(lower_signedness(signedness))
SubSaturating(signedness) => SaturatingSub(lower_signedness(signedness))
Min(signedness) => Min(lower_signedness(signedness))
Max(signedness) => Max(lower_signedness(signedness))
AverageUnsigned => AverageUnsigned
ExtMul(half, signedness) =>
ExtendMultiply(lower_vector_half(half), lower_signedness(signedness))
Dot16To32Signed => Dot16To32Signed
Q15MulrSaturating => Q15MultiplyRoundedSaturating
}
IntBinary(lane, operation)
}
///|
fn lower_int_shift_vector(
operation : @milkir.VectorIntShiftOp,
lane : @milkir.VectorIntLane,
) -> @lowering.VectorOp {
let operation : @lowering.VectorIntShiftOp = match operation {
Left => Left
Right(signedness) => Right(lower_signedness(signedness))
}
IntShift(lower_integer_lane(lane), operation)
}
///|
fn lower_int_compare_vector(
operation : @milkir.VectorIntCompareOp,
lane : @milkir.VectorIntLane,
) -> @lowering.VectorOp {
let operation : @lowering.VectorIntComparison = match operation {
Eq => Equal
Ne => NotEqual
Lt(signedness) => LessThan(lower_signedness(signedness))
Le(signedness) => LessOrEqual(lower_signedness(signedness))
Gt(signedness) => GreaterThan(lower_signedness(signedness))
Ge(signedness) => GreaterOrEqual(lower_signedness(signedness))
}
IntCompare(lower_integer_lane(lane), operation)
}
///|
fn lower_float_unary_vector(
operation : @milkir.VectorFloatUnaryOp,
lane : @milkir.VectorFloatLane,
) -> @lowering.VectorOp {
let operation : @lowering.VectorFloatUnaryOp = match operation {
Abs => Absolute
Neg => Negate
Sqrt => SquareRoot
Ceil => Ceil
Floor => Floor
Trunc => Truncate
Nearest => Nearest
}
FloatUnary(lower_float_lane(lane), operation)
}
///|
fn lower_float_binary_vector(
operation : @milkir.VectorFloatBinaryOp,
lane : @milkir.VectorFloatLane,
) -> @lowering.VectorOp {
let operation : @lowering.VectorFloatBinaryOp = match operation {
Add => Add
Sub => Sub
Mul => Mul
Div => Div
Min => Min
Max => Max
PseudoMin => PseudoMin
PseudoMax => PseudoMax
}
FloatBinary(lower_float_lane(lane), operation)
}
///|
fn lower_float_compare_vector(
operation : @milkir.VectorFloatCompareOp,
lane : @milkir.VectorFloatLane,
) -> @lowering.VectorOp {
let operation : @lowering.VectorFloatComparison = match operation {
Eq => Equal
Ne => NotEqual
Lt => LessThan
Le => LessOrEqual
Gt => GreaterThan
Ge => GreaterOrEqual
}
FloatCompare(lower_float_lane(lane), operation)
}
///|
fn lower_vector_conversion(
operation : @milkir.VectorConversionOp,
) -> @lowering.VectorOp {
Convert(
match operation {
TruncSatF32ToI32(signedness) =>
FloatToInt(F32x4, I32x4, lower_signedness(signedness), Saturating)
TruncSatF64ToI32Zero(signedness) =>
FloatToInt(F64x2, I32x4, lower_signedness(signedness), Saturating)
ConvertI32ToF32(signedness) =>
IntToFloat(I32x4, F32x4, lower_signedness(signedness))
ConvertLowI32ToF64(signedness) =>
IntToFloat(I32x4, F64x2, lower_signedness(signedness))
DemoteF64ToF32Zero => DemoteZeroF64x2
PromoteLowF32ToF64 => PromoteLowF32x4
},
)
}
///|
fn lower_relaxed_vector(
operation : @milkir.VectorRelaxedOp,
) -> @lowering.VectorOp {
Relaxed(
match operation {
Swizzle => Swizzle
TruncF32ToI32(signedness) =>
FloatToInt(F32x4, I32x4, lower_signedness(signedness))
TruncF64ToI32Zero(signedness) =>
FloatToInt(F64x2, I32x4, lower_signedness(signedness))
Fma(lane, operation) =>
FusedMultiplyAdd(
lower_float_lane(lane),
match operation {
Add => FusedMultiplyAdd
NegatedAdd => FusedNegatedMultiplyAdd
},
)
LaneSelect(lane) => LaneSelect(lower_integer_lane(lane))
Min(lane) => Min(lower_float_lane(lane))
Max(lane) => Max(lower_float_lane(lane))
Q15MulrSigned => Q15MultiplyRoundedSigned
Dot8To16Signed => Dot8To16Signed
Dot8To32AddSigned => Dot8To32AddSigned
},
)
}
///|
fn lower_vector_operation(operation : @milkir.VectorOp) -> @lowering.Operation {
match operation {
Const(bytes) => V128Const(bytes_word(bytes, 0), bytes_word(bytes, 8))
Splat(lane) => Vector(Splat(lower_lane(lane)))
ExtractLane(lane, extension, index) =>
Vector(ExtractLane(lower_lane(lane), index, lower_extension(extension)))
ReplaceLane(lane, index) => Vector(ReplaceLane(lower_lane(lane), index))
Shuffle(mask) => Vector(Shuffle(mask))
Swizzle => Vector(Swizzle)
Bitwise(operation) =>
Vector(
Bitwise(
match operation {
Not => Not
And => And
Or => Or
Xor => Xor
Bitselect => BitSelect
},
),
)
Predicate(operation) =>
Vector(
Predicate(
match operation {
AnyTrue => AnyTrue
AllTrue(lane) => AllTrue(lower_integer_lane(lane))
Bitmask(lane) => BitMask(lower_integer_lane(lane))
},
),
)
IntUnary(operation, lane) => Vector(lower_int_unary_vector(operation, lane))
IntBinary(operation, lane) =>
Vector(lower_int_binary_vector(operation, lane))
IntShift(operation, lane) => Vector(lower_int_shift_vector(operation, lane))
IntCompare(operation, lane) =>
Vector(lower_int_compare_vector(operation, lane))
Narrow(lane, signedness) =>
Vector(
Convert(Narrow(lower_integer_lane(lane), lower_signedness(signedness))),
)
FloatUnary(operation, lane) =>
Vector(lower_float_unary_vector(operation, lane))
FloatBinary(operation, lane) =>
Vector(lower_float_binary_vector(operation, lane))
FloatCompare(operation, lane) =>
Vector(lower_float_compare_vector(operation, lane))
Convert(operation) => Vector(lower_vector_conversion(operation))
Relaxed(operation) => Vector(lower_relaxed_vector(operation))
}
}
///|
fn lower_vector_instruction(
builder : @lowering.DirectBuilder,
values : Array[@lowering.Value?],
value_types : Array[@native_types.ValueType?],
facts : SelectionFacts,
instruction : @milkir.Inst,
operation : @milkir.VectorOp,
) -> Unit raise NativeLowerError {
let (selected_operation, operands) = match
select_vector_andnot(instruction, facts) {
Some((left, right, negated)) if should_defer_instruction(
facts.definitions[negated.id].unwrap(),
facts,
) => (@lowering.Operation::Vector(Bitwise(AndNot)), [left, right])
_ => (lower_vector_operation(operation), instruction.args)
}
let lowered_results = builder.emit_with_metadata(
selected_operation,
demand_values(builder, values, value_types, facts, operands),
instruction.results.map(value => lower_type(value.ty)),
instruction_metadata(instruction, []),
)
assign_instruction_results(values, instruction, lowered_results)
}
///|
fn widened_lane(lane : @milkir.VectorIntLane) -> @lowering.VectorLane {
match lane {
I8 => I16x8
I16 => I32x4
I32 => I64x2
I64 => I64x2
}
}
///|
fn lower_vector_memory_instruction(
builder : @lowering.DirectBuilder,
values : Array[@lowering.Value?],
value_types : Array[@native_types.ValueType?],
facts : SelectionFacts,
instruction : @milkir.Inst,
operation : @milkir.VectorMemoryOp,
) -> Unit raise NativeLowerError {
let address = lower_pointer_value(
builder,
values,
value_types,
facts,
instruction.args[0],
instruction_source(instruction),
instruction.id,
)
let (operation, operands, result_types) : (
@lowering.Operation,
Array[@lowering.Value],
Array[@native_types.ValueType],
) = match operation {
LoadExtend(lane, signedness) =>
(
VectorLoad(
@lowering.VectorLoadSpec::new(
Extend(widened_lane(lane), lower_signedness(signedness)),
0UL,
Little,
None,
),
),
[address],
[V128],
)
LoadSplat(lane) =>
(
VectorLoad(
@lowering.VectorLoadSpec::new(
Splat(lower_integer_lane(lane)),
0UL,
Little,
None,
),
),
[address],
[V128],
)
LoadZero(lane) =>
(
VectorLoad(
@lowering.VectorLoadSpec::new(
Zero(if lane is I32 { W32 } else { W64 }),
0UL,
Little,
None,
),
),
[address],
[V128],
)
LoadLane(lane, index) =>
(
VectorLoad(
@lowering.VectorLoadSpec::new(
Lane(lower_integer_lane(lane), index),
0UL,
Little,
None,
),
),
[
address,
demand_value(builder, values, value_types, facts, instruction.args[1]),
],
[V128],
)
StoreLane(lane, index) =>
(
VectorStoreLane(
@lowering.VectorStoreLaneSpec::new(
lower_integer_lane(lane),
index,
0UL,
Little,
None,
),
),
[
address,
demand_value(builder, values, value_types, facts, instruction.args[1]),
],
[],
)
}
let lowered_results = builder.emit_with_metadata(
operation,
operands,
result_types,
instruction_metadata(instruction, []),
)
assign_instruction_results(values, instruction, lowered_results)
}
///|
// The third value is the negation result absorbed by this selection.
fn select_vector_andnot(
instruction : @milkir.Inst,
facts : SelectionFacts,
) -> (@milkir.Value, @milkir.Value, @milkir.Value)? {
guard instruction.opcode is Vector(Bitwise(And)) &&
instruction.args is [left, right] &&
left != right else {
return None
}
if vector_not_source(right, facts) is Some(source) {
return Some((left, source, right))
}
if vector_not_source(left, facts) is Some(source) {
return Some((right, source, left))
}
None
}
///|
fn vector_not_is_fully_selected(
value : @milkir.Value,
facts : SelectionFacts,
) -> Bool {
fn selected(consumer : Int32) -> Bool {
guard consumer >= 0 && facts.instructions[consumer] is Some(instruction) else {
return false
}
match select_vector_andnot(instruction, facts) {
Some((_, _, absorbed)) => absorbed == value
None => false
}
}
if facts.uses[value.id] == 1 {
return selected(facts.consumers[value.id])
}
facts.shared_consumers[value.id] is Some(consumers) && consumers.all(selected)
}
///|
fn vector_not_source(
value : @milkir.Value,
facts : SelectionFacts,
) -> @milkir.Value? {
guard facts.definitions[value.id] is Some(definition) &&
definition.opcode is Vector(Bitwise(Not)) else {
return None
}
unary_operand(definition)
}