//===- arm_mve.td - ACLE intrinsic functions for MVE architecture ---------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the set of ACLE-specified source-level intrinsic // functions wrapping the MVE vector instruction set and scalar shift // operations. // // Refer to comments in arm_mve_defs.td for the infrastructure used in // here, and to MveEmitter.cpp for how those are used in turn to // generate code. // //===----------------------------------------------------------------------===// include "arm_mve_defs.td" let params = T.Usual in foreach n = [ 2, 4 ] in { def "vst"#n#"q": Intrinsic, MultiVector), (CustomCodegen<"VST24"> n:$NumVectors, "Intrinsic::arm_mve_vst"#n#"q":$IRIntr)>; def "vld"#n#"q": Intrinsic, (args CPtr), (CustomCodegen<"VLD24"> n:$NumVectors, "Intrinsic::arm_mve_vld"#n#"q":$IRIntr)>; } multiclass bit_op_fp { def "": Intrinsic; } multiclass bit_op_fp_with_inv { def "": Intrinsic; } let params = T.Signed in { def vqaddq: Intrinsic $a, $b)>; def vqsubq: Intrinsic $a, $b)>; } let params = T.Unsigned in { def vqaddq_u: Intrinsic $a, $b)>, NameOverride<"vqaddq">; def vqsubq_u: Intrinsic $a, $b)>, NameOverride<"vqsubq">; } // Some intrinsics below are implemented not as IR fragments, but as // special-purpose IR intrinsics. This is because such a general form // (such as NEON uses) required a variable-width vector size, and we are // restricted to 128 bit. Although we can possibly get clever with lane // operations, the consequent IR representation would be very hard to // write sensibly. In particular, doubling a vector's width would be a // mess. Other intrinsics just don't translate nicely into IR. let params = T.Int in { def vaddq: Intrinsic; def vhaddq: Intrinsic $a, $b, (unsignedflag Scalar))>; def vrhaddq: Intrinsic $a, $b, (unsignedflag Scalar))>; def vandq: Intrinsic; def vbicq: Intrinsic; def veorq: Intrinsic; def vornq: Intrinsic; def vorrq: Intrinsic; def vsubq: Intrinsic; def vhsubq: Intrinsic $a, $b, (unsignedflag Scalar))>; def vmulq: Intrinsic; def vmulhq: Intrinsic $a, $b, (unsignedflag Scalar))>; def vrmulhq: Intrinsic $a, $b, (unsignedflag Scalar))>; def vmullbq_int: Intrinsic $a, $b, (unsignedflag Scalar), 0)>; def vmulltq_int: Intrinsic $a, $b, (unsignedflag Scalar), 1)>; } let params = T.Signed in { def vqdmulhq: Intrinsic $a, $b)>; def vqrdmulhq: Intrinsic $a, $b)>; } let params = T.Poly, overrideKindLetter = "p" in { def vmullbq_poly: Intrinsic $a, $b, 0)>; def vmulltq_poly: Intrinsic $a, $b, 1)>; } let params = T.Float in { def vaddqf: Intrinsic, NameOverride<"vaddq">; defm vandqf: bit_op_fp, NameOverride<"vandq">; defm vbicqf: bit_op_fp_with_inv, NameOverride<"vbicq">; defm veorqf: bit_op_fp, NameOverride<"veorq">; defm vornqf: bit_op_fp_with_inv, NameOverride<"vornq">; defm vorrqf: bit_op_fp, NameOverride<"vorrq">; def vsubqf: Intrinsic, NameOverride<"vsubq">; def vmulqf: Intrinsic, NameOverride<"vmulq">; } // The bitcasting below is not overcomplicating the IR because while // Vector and UVector may be different vector types at the C level i.e. // vectors of same size signed/unsigned ints. Once they're lowered // to IR, they are just bit vectors with no sign at all, so the // bitcasts will be automatically elided by IRBuilder. multiclass predicated_bit_op_fp { def "": Intrinsic (bitcast $a, UVector), (bitcast $b, UVector), $pred, (bitcast $inactive, UVector)), Vector)>; } // Plain intrinsics let params = T.Usual in { def vabdq: Intrinsic $a, $b, (unsignedflag Scalar))>; } multiclass VectorVectorArithmetic { defm "" : IntrinsicMX< Vector, (args Vector:$a, Vector:$b, Predicate:$pred), !con((IRInt $a, $b), extraArgs, (? $pred, $inactive)), wantXVariant>; } multiclass VectorVectorArithmeticBitcast { defm "" : IntrinsicMX (bitcast $a, UVector), (bitcast $b, UVector), $pred, (bitcast $inactive, UVector)), Vector)>; } // Predicated intrinsics let params = T.Usual in { defm vabdq : VectorVectorArithmetic<"abd_predicated", (? (unsignedflag Scalar))>; defm vaddq : VectorVectorArithmetic<"add_predicated">; defm vsubq : VectorVectorArithmetic<"sub_predicated">; defm vmulq : VectorVectorArithmetic<"mul_predicated">; defm vandq : VectorVectorArithmeticBitcast<"and_predicated">; defm vbicq : VectorVectorArithmeticBitcast<"bic_predicated">; defm veorq : VectorVectorArithmeticBitcast<"eor_predicated">; defm vornq : VectorVectorArithmeticBitcast<"orn_predicated">; defm vorrq : VectorVectorArithmeticBitcast<"orr_predicated">; } multiclass DblVectorVectorArithmetic { defm "" : IntrinsicMX< DblVector, (args Vector:$a, Vector:$b, Predicate:$pred), !con((IRInt $a, $b), extraArgs, (? $pred, $inactive))>; } // Predicated intrinsics - Int types only let params = T.Int in { defm vminq : VectorVectorArithmetic<"min_predicated", (? (unsignedflag Scalar))>; defm vmaxq : VectorVectorArithmetic<"max_predicated", (? (unsignedflag Scalar))>; defm vmulhq : VectorVectorArithmetic<"mulh_predicated", (? (unsignedflag Scalar))>; defm vrmulhq : VectorVectorArithmetic<"rmulh_predicated", (? (unsignedflag Scalar))>; defm vqaddq : VectorVectorArithmetic<"qadd_predicated", (? (unsignedflag Scalar)), 0>; defm vhaddq : VectorVectorArithmetic<"hadd_predicated", (? (unsignedflag Scalar))>; defm vrhaddq : VectorVectorArithmetic<"rhadd_predicated", (? (unsignedflag Scalar))>; defm vqsubq : VectorVectorArithmetic<"qsub_predicated", (? (unsignedflag Scalar)), 0>; defm vhsubq : VectorVectorArithmetic<"hsub_predicated", (? (unsignedflag Scalar))>; defm vmullbq_int : DblVectorVectorArithmetic<"mull_int_predicated", (? (unsignedflag Scalar), (u32 0))>; defm vmulltq_int : DblVectorVectorArithmetic<"mull_int_predicated", (? (unsignedflag Scalar), (u32 1))>; } let params = T.Signed in { defm vqdmulhq : VectorVectorArithmetic<"qdmulh_predicated", (?), 0>; defm vqrdmulhq : VectorVectorArithmetic<"qrdmulh_predicated", (?), 0>; } let params = T.Poly, overrideKindLetter = "p" in { defm vmullbq_poly : DblVectorVectorArithmetic<"mull_poly_predicated", (? (u32 0))>; defm vmulltq_poly : DblVectorVectorArithmetic<"mull_poly_predicated", (? (u32 1))>; } // Predicated intrinsics - Float types only let params = T.Float in { defm vminnmq : VectorVectorArithmetic<"min_predicated", (? (u32 0))>; defm vmaxnmq : VectorVectorArithmetic<"max_predicated", (? (u32 0))>; } let params = T.Int in { def vminvq: Intrinsic $prev, $vec))>; def vmaxvq: Intrinsic $prev, $vec))>; } foreach half = [ "b", "t" ] in { defvar halfconst = !if(!eq(half, "b"), 0, 1); let params = [f32], pnt = PNT_None in { def vcvt#half#q_f16: Intrinsic< VecOf, (args VecOf:$inactive, Vector:$a), (IRInt<"vcvt_narrow"> $inactive, $a, halfconst)>; def vcvt#half#q_m_f16: Intrinsic< VecOf, (args VecOf:$inactive, Vector:$a, PredOf:$pred), (IRInt<"vcvt_narrow_predicated"> $inactive, $a, halfconst, $pred)>; } // params = [f32], pnt = PNT_None } // loop over half = "b", "t" multiclass compare_with_pred { // Make the predicated and unpredicated versions of a single comparison. def: Intrinsic cmp))>, NameOverride<"vcmp" # condname # "q" # suffix>; def: Intrinsic (and $inpred, cmp)))>, NameOverride<"vcmp" # condname # "q_m" # suffix>; } multiclass compare { // Make all four variants of a comparison: the vector/vector and // vector/scalar forms, each using compare_with_pred to make a // predicated and unpredicated version. defm: compare_with_pred; let pnt = PNT_NType in { defm: compare_with_pred:$sb), (cmpop $va, (splat $sb)), "_n">; } } let params = T.Int in { defm: compare<"eq", icmp_eq>; defm: compare<"ne", icmp_ne>; } let params = T.Signed in { defm: compare<"gt", icmp_sgt>; defm: compare<"ge", icmp_sge>; defm: compare<"lt", icmp_slt>; defm: compare<"le", icmp_sle>; } let params = T.Unsigned in { defm: compare<"hi", icmp_ugt>; defm: compare<"cs", icmp_uge>; } let params = T.Float in { defm: compare<"eq", fcmp_eq>; defm: compare<"ne", fcmp_ne>; defm: compare<"gt", fcmp_gt>; defm: compare<"ge", fcmp_ge>; defm: compare<"lt", fcmp_lt>; defm: compare<"le", fcmp_le>; } let params = T.Signed in { def vminq: Intrinsic; def vmaxq: Intrinsic; } let params = T.Unsigned in { def vminqu: Intrinsic, NameOverride<"vminq">; def vmaxqu: Intrinsic, NameOverride<"vmaxq">; } let params = T.Float in { def vminnmq: Intrinsic $a, $b)>; def vmaxnmq: Intrinsic $a, $b)>; } def vpselq: Intrinsic { let params = T.Usual; } def vpselq_64: Intrinsic< Vector, (args Vector:$t, Vector:$f, PredOf:$pred), (bitcast (select $pred, (bitcast $t, VecOf), (bitcast $f, VecOf)), Vector)>, NameOverride<"vpselq"> { let params = T.All64; } let params = [Void], pnt = PNT_None in { multiclass vctp { def "": Intrinsic (IRIntBase $val)))>; def _m: Intrinsic (and $inpred, (IRIntBase $val))))>; } defm vctp8q: vctp, "arm_mve_vctp8">; defm vctp16q: vctp, "arm_mve_vctp16">; defm vctp32q: vctp, "arm_mve_vctp32">; defm vctp64q: vctp, "arm_mve_vctp64">; def vpnot: Intrinsic, (args unpromoted>:$pred), (xor $pred, (u16 65535))>; } multiclass contiguous_load same_size, list wider> { // Intrinsics named with explicit memory and element sizes that match: // vldrbq_?8, vldrhq_?16, vldrwq_?32. let params = same_size, pnt = PNT_None in { def: Intrinsic>:$addr), (load (address (CPtr $addr), !srl(memtype.size,3)))>, NameOverride; def: Intrinsic>:$addr, Predicate:$pred), (IRIntBase<"masked_load", [Vector, CPtr]> (CPtr $addr), !srl(memtype.size,3), $pred, (zeroinit Vector))>, NameOverride; } // Synonyms for the above, with the generic name vld1q that just means // 'memory and element sizes match', and allows convenient polymorphism with // the memory and element types covariant. let params = same_size in { def: Intrinsic>:$addr), (load (address (CPtr $addr), !srl(memtype.size,3)))>, NameOverride<"vld1q">; def: Intrinsic>:$addr, Predicate:$pred), (IRIntBase<"masked_load", [Vector, CPtr]> (CPtr $addr), !srl(memtype.size,3), $pred, (zeroinit Vector))>, NameOverride<"vld1q_z">; } // Intrinsics with the memory size narrower than the vector element, so that // they load less than 128 bits of memory and sign/zero extend each loaded // value into a wider vector lane. let params = wider, pnt = PNT_None in { def: Intrinsic>:$addr), (extend (load (address (CPtr> $addr), !srl(memtype.size,3))), Vector, (unsignedflag Scalar))>, NameOverride; def: Intrinsic>:$addr, Predicate:$pred), (extend (IRIntBase<"masked_load", [NarrowedVecOf, CPtr>]> (CPtr> $addr), !srl(memtype.size,3), $pred, (zeroinit NarrowedVecOf)), Vector, (unsignedflag Scalar))>, NameOverride; } } defm: contiguous_load<"vldrbq", u8, T.All8, !listconcat(T.Int16, T.Int32)>; defm: contiguous_load<"vldrhq", u16, T.All16, T.Int32>; defm: contiguous_load<"vldrwq", u32, T.All32, []>; multiclass contiguous_store same_size, list wider> { // Intrinsics named with explicit memory and element sizes that match: // vstrbq_?8, vstrhq_?16, vstrwq_?32. let params = same_size in { def: Intrinsic>:$addr, Vector:$value), (store $value, (address (Ptr $addr), !srl(memtype.size,3)))>, NameOverride; def: Intrinsic>:$addr, Vector:$value, Predicate:$pred), (IRIntBase<"masked_store", [Vector, Ptr]> $value, (Ptr $addr), !srl(memtype.size,3), $pred)>, NameOverride; } // Synonyms for the above, with the generic name vst1q that just means // 'memory and element sizes match', and allows convenient polymorphism with // the memory and element types covariant. let params = same_size in { def: Intrinsic>:$addr, Vector:$value), (store $value, (address (Ptr $addr), !srl(memtype.size,3)))>, NameOverride<"vst1q">; def: Intrinsic>:$addr, Vector:$value, Predicate:$pred), (IRIntBase<"masked_store", [Vector, Ptr]> $value, (Ptr $addr), !srl(memtype.size,3), $pred)>, NameOverride<"vst1q_p">; } // Intrinsics with the memory size narrower than the vector element, so that // they store less than 128 bits of memory, truncating each vector lane into // a narrower value to store. let params = wider in { def: Intrinsic>:$addr, Vector:$value), (store (trunc $value, NarrowedVecOf), (address (Ptr> $addr), !srl(memtype.size,3)))>, NameOverride; def: Intrinsic>:$addr, Vector:$value, Predicate:$pred), (IRIntBase<"masked_store", [NarrowedVecOf, Ptr>]> (trunc $value, NarrowedVecOf), (Ptr> $addr), !srl(memtype.size,3), $pred)>, NameOverride; } } defm: contiguous_store<"vstrbq", u8, T.All8, !listconcat(T.Int16, T.Int32)>; defm: contiguous_store<"vstrhq", u16, T.All16, T.Int32>; defm: contiguous_store<"vstrwq", u32, T.All32, []>; multiclass gather_base types, int size> { let params = types, pnt = PNT_None in { def _gather_base: Intrinsic< Vector, (args UVector:$addr, imm_mem7bit:$offset), (IRInt<"vldr_gather_base", [Vector, UVector]> $addr, $offset)>; def _gather_base_z: Intrinsic< Vector, (args UVector:$addr, imm_mem7bit:$offset, Predicate:$pred), (IRInt<"vldr_gather_base_predicated", [Vector, UVector, Predicate]> $addr, $offset, $pred)>; def _gather_base_wb: Intrinsic< Vector, (args Ptr:$addr, imm_mem7bit:$offset), (seq (IRInt<"vldr_gather_base_wb", [Vector, UVector]> (load $addr), $offset):$pair, (store (xval $pair, 1), $addr), (xval $pair, 0))>; def _gather_base_wb_z: Intrinsic< Vector, (args Ptr:$addr, imm_mem7bit:$offset, Predicate:$pred), (seq (IRInt<"vldr_gather_base_wb_predicated", [Vector, UVector, Predicate]> (load $addr), $offset, $pred):$pair, (store (xval $pair, 1), $addr), (xval $pair, 0))>; } } defm vldrwq: gather_base; defm vldrdq: gather_base; multiclass scatter_base types, int size> { let params = types in { def _scatter_base: Intrinsic< Void, (args UVector:$addr, imm_mem7bit:$offset, Vector:$data), (IRInt<"vstr_scatter_base", [UVector, Vector]> $addr, $offset, $data)>; def _scatter_base_p: Intrinsic< Void, (args UVector:$addr, imm_mem7bit:$offset, Vector:$data, Predicate:$pred), (IRInt<"vstr_scatter_base_predicated", [UVector, Vector, Predicate]> $addr, $offset, $data, $pred)>; def _scatter_base_wb: Intrinsic< Void, (args Ptr:$addr, imm_mem7bit:$offset, Vector:$data), (seq (IRInt<"vstr_scatter_base_wb", [UVector, Vector]> (load $addr), $offset, $data):$wbaddr, (store $wbaddr, $addr))>; def _scatter_base_wb_p: Intrinsic< Void, (args Ptr:$addr, imm_mem7bit:$offset, Vector:$data, Predicate:$pred), (seq (IRInt<"vstr_scatter_base_wb_predicated", [UVector, Vector, Predicate]> (load $addr), $offset, $data, $pred):$wbaddr, (store $wbaddr, $addr))>; } } defm vstrwq: scatter_base; defm vstrdq: scatter_base; multiclass gather_offset_unshifted types, PrimitiveType memtype> { let params = types in { def _gather_offset: Intrinsic< Vector, (args CPtr>:$base, UVector:$offsets), (IRInt<"vldr_gather_offset", [Vector, CPtr>, UVector]> $base, $offsets, memtype.size, 0, (unsignedflag Scalar))>; def _gather_offset_z: Intrinsic< Vector, (args CPtr>:$base, UVector:$offsets, Predicate:$pred), (IRInt<"vldr_gather_offset_predicated", [Vector, CPtr>, UVector, Predicate]> $base, $offsets, memtype.size, 0, (unsignedflag Scalar), $pred)>; } } multiclass gather_offset_shifted types, PrimitiveType memtype, int shift> { let params = types in { def _gather_shifted_offset: Intrinsic< Vector, (args CPtr>:$base, UVector:$offsets), (IRInt<"vldr_gather_offset", [Vector, CPtr>, UVector]> $base, $offsets, memtype.size, shift, (unsignedflag Scalar))>; def _gather_shifted_offset_z: Intrinsic< Vector, (args CPtr>:$base, UVector:$offsets, Predicate:$pred), (IRInt<"vldr_gather_offset_predicated", [Vector, CPtr>, UVector, Predicate]> $base, $offsets, memtype.size, shift, (unsignedflag Scalar), $pred)>; } } multiclass gather_offset_both types, PrimitiveType memtype, int shift> { defm "": gather_offset_unshifted; defm "": gather_offset_shifted; } defm vldrbq: gather_offset_unshifted; defm vldrhq: gather_offset_both; defm vldrwq: gather_offset_both; defm vldrdq: gather_offset_both; multiclass scatter_offset_unshifted types, PrimitiveType memtype> { let params = types in { def _scatter_offset: Intrinsic< Void, (args Ptr>:$base, UVector:$offsets, Vector:$data), (IRInt<"vstr_scatter_offset", [Ptr>, UVector, Vector]> $base, $offsets, $data, memtype.size, 0)>; def _scatter_offset_p: Intrinsic< Void, (args Ptr>:$base, UVector:$offsets, Vector:$data, Predicate:$pred), (IRInt<"vstr_scatter_offset_predicated", [Ptr>, UVector, Vector, Predicate]> $base, $offsets, $data, memtype.size, 0, $pred)>; } } multiclass scatter_offset_shifted types, PrimitiveType memtype, int shift> { let params = types in { def _scatter_shifted_offset: Intrinsic< Void, (args Ptr>:$base, UVector:$offsets, Vector:$data), (IRInt<"vstr_scatter_offset", [Ptr>, UVector, Vector]> $base, $offsets, $data, memtype.size, shift)>; def _scatter_shifted_offset_p: Intrinsic< Void, (args Ptr>:$base, UVector:$offsets, Vector:$data, Predicate:$pred), (IRInt<"vstr_scatter_offset_predicated", [Ptr>, UVector, Vector, Predicate]> $base, $offsets, $data, memtype.size, shift, $pred)>; } } multiclass scatter_offset_both types, PrimitiveType memtype, int shift> { defm "": scatter_offset_unshifted; defm "": scatter_offset_shifted; } defm vstrbq: scatter_offset_unshifted; defm vstrhq: scatter_offset_both; defm vstrwq: scatter_offset_both; defm vstrdq: scatter_offset_both; let params = T.Int in { def vshlq_n: Intrinsic; defm vshlq: IntrinsicMX $v, $sh, $pred, $inactive), 1, "_n">; let pnt = PNT_NType in { def vshrq_n: Intrinsic; defm vshrq: IntrinsicMX $v, $sh, (unsignedflag Scalar), $pred, $inactive), 1, "_n">; } } let params = T.Int in { def vqshlq_n: Intrinsic $v, $sh, (unsignedflag Scalar))>; def vqshlq_m_n: Intrinsic $v, $sh, (unsignedflag Scalar), $pred, $inactive)>; let pnt = PNT_NType in { def vrshrq_n: Intrinsic $v, $sh, (unsignedflag Scalar))>; defm vrshrq: IntrinsicMX $v, $sh, (unsignedflag Scalar), $pred, $inactive), 1, "_n">; } } let params = T.Signed, pnt = PNT_NType in { def vqshluq_n: Intrinsic $v, $sh)>; def vqshluq_m_n: Intrinsic $v, $sh, $pred, $inactive)>; } multiclass vshll_imm { let params = !listconcat(T.Int8, T.Int16), pnt = PNT_NType in { def _n: Intrinsic $v, $sh, (unsignedflag Scalar), top)>; defm "": IntrinsicMX $v, $sh, (unsignedflag Scalar), top, $pred, $inactive), 1, "_n">; } } defm vshllbq : vshll_imm<0>; defm vshlltq : vshll_imm<1>; multiclass DyadicImmShift { defvar intparams = !if(!eq(!cast(outtype), !cast(Vector)), [Vector], [outtype, Vector]); def q_n: Intrinsic< outtype, (args outtype:$a, Vector:$b, imm:$sh), !con((IRInt $a, $b, $sh), extraargs)>; def q_m_n: Intrinsic< outtype, (args outtype:$a, Vector:$b, imm:$sh, Predicate:$pred), !con((IRInt $a, $b, $sh), extraargs, (? $pred))>; } multiclass VSHRN { defm b: DyadicImmShift; defm t: DyadicImmShift; } let params = [s16, s32, u16, u32], pnt = PNT_NType in { defvar U = (unsignedflag Scalar); defm vshrn : VSHRN; defm vqshrn : VSHRN; defm vrshrn : VSHRN; defm vqrshrn : VSHRN; } let params = [s16, s32], pnt = PNT_NType in { defm vqshrun : VSHRN; defm vqrshrun : VSHRN; } let params = T.Int, pnt = PNT_NType in { defm vsli : DyadicImmShift; defm vsri : DyadicImmShift; } multiclass VSHL_non_imm { let pnt = pnt_scalar_unpred in { def scalarSuffix: Intrinsic< Vector, (args Vector:$in, s32:$sh), (IRInt<"vshl_scalar", [Vector]> $in, $sh, q, r, (unsignedflag Scalar))>; } def "_m" # scalarSuffix: Intrinsic< Vector, (args Vector:$in, s32:$sh, Predicate:$pred), (IRInt<"vshl_scalar_predicated", [Vector, Predicate]> $in, $sh, q, r, (unsignedflag Scalar), $pred)>; def "": Intrinsic< Vector, (args Vector:$in, SVector:$sh), (IRInt<"vshl_vector", [Vector, SVector]> $in, $sh, q, r, (unsignedflag Scalar))>; defm "": IntrinsicMX< Vector, (args Vector:$in, SVector:$sh, Predicate:$pred), (IRInt<"vshl_vector_predicated", [Vector, SVector, Predicate]> $in, $sh, q, r, (unsignedflag Scalar), $pred, $inactive), // The saturating shift intrinsics don't have an x variant, so we // set wantXVariant to 1 iff q == 0 !eq(q, 0)>; } let params = T.Int in { defm vshlq : VSHL_non_imm<"_r", 0, 0>; defm vqshlq : VSHL_non_imm<"_r", 1, 0>; defm vrshlq : VSHL_non_imm<"_n", 0, 1, PNT_NType>; defm vqrshlq : VSHL_non_imm<"_n", 1, 1, PNT_NType>; } // Base class for the scalar shift intrinsics. class ScalarShift: Intrinsic { let params = [Void]; let pnt = PNT_None; } // Subclass that includes the machinery to take a 64-bit input apart // into halves, retrieve the two halves of a shifted output as a pair, // and glue the pieces of the pair back into an i64 for output. class LongScalarShift: ScalarShift; // The family of saturating/rounding scalar shifts that take an // immediate shift count. They come in matched 32- and 64-bit pairs. multiclass ScalarSaturatingShiftImm { def "": ScalarShift $value, $sh)>; def l: LongScalarShift $lo, $hi, $sh)>; } defm uqshl: ScalarSaturatingShiftImm; defm urshr: ScalarSaturatingShiftImm; defm sqshl: ScalarSaturatingShiftImm; defm srshr: ScalarSaturatingShiftImm; // The family of saturating/rounding scalar shifts that take a // register shift count. They also have 32- and 64-bit forms, but the // 64-bit form also has a version that saturates to 48 bits, so the IR // intrinsic takes an extra saturation-type operand. multiclass ScalarSaturatingShiftReg { def "": ScalarShift $value, $sh)>; def l: LongScalarShift $lo, $hi, $sh, 64)>; def l_sat48: LongScalarShift $lo, $hi, $sh, 48)>; } defm uqrshl: ScalarSaturatingShiftReg; defm sqrshr: ScalarSaturatingShiftReg; // The intrinsics for LSLL and ASRL come in 64-bit versions only, with // no saturation count. def lsll: LongScalarShift $lo, $hi, $sh)>; def asrl: LongScalarShift $lo, $hi, $sh)>; let params = T.Int32 in { def vadcq: Intrinsic:$carry), (seq (IRInt<"vadc", [Vector]> $a, $b, (shl (load $carry), 29)):$pair, (store (and 1, (lshr (xval $pair, 1), 29)), $carry), (xval $pair, 0))>; def vadciq: Intrinsic:$carry), (seq (IRInt<"vadc", [Vector]> $a, $b, 0):$pair, (store (and 1, (lshr (xval $pair, 1), 29)), $carry), (xval $pair, 0))>; def vadcq_m: Intrinsic:$carry, Predicate:$pred), (seq (IRInt<"vadc_predicated", [Vector, Predicate]> $inactive, $a, $b, (shl (load $carry), 29), $pred):$pair, (store (and 1, (lshr (xval $pair, 1), 29)), $carry), (xval $pair, 0))>; def vadciq_m: Intrinsic:$carry, Predicate:$pred), (seq (IRInt<"vadc_predicated", [Vector, Predicate]> $inactive, $a, $b, 0, $pred):$pair, (store (and 1, (lshr (xval $pair, 1), 29)), $carry), (xval $pair, 0))>; } multiclass VectorComplexAddPred { def "" : Intrinsic not_halving, angle, $a, $b)>; defm "" : IntrinsicMX not_halving, angle, $inactive, $a, $b, $pred)>; } multiclass VectorComplexMulPred { def "" : Intrinsic angle, $a, $b)>; defm "" : IntrinsicMX angle, $inactive, $a, $b, $pred)>; } multiclass VectorComplexMLAPred { def "" : Intrinsic angle, $a, $b, $c)>; def _m : Intrinsic angle, $a, $b, $c, $pred)>; } multiclass VectorComplexAddAngle { defm _rot90 : VectorComplexAddPred; defm _rot270 : VectorComplexAddPred; } multiclass VectorComplexMulAngle { defm "" : VectorComplexMulPred<(u32 0)>; defm _rot90 : VectorComplexMulPred<(u32 1)>; defm _rot180 : VectorComplexMulPred<(u32 2)>; defm _rot270 : VectorComplexMulPred<(u32 3)>; } multiclass VectorComplexMLAAngle { defm "" : VectorComplexMLAPred<(u32 0)>; defm _rot90 : VectorComplexMLAPred<(u32 1)>; defm _rot180 : VectorComplexMLAPred<(u32 2)>; defm _rot270 : VectorComplexMLAPred<(u32 3)>; } let params = T.Usual in defm vcaddq : VectorComplexAddAngle<(u32 1)>; let params = T.Signed in defm vhcaddq : VectorComplexAddAngle<(u32 0)>; let params = T.Float in { defm vcmulq : VectorComplexMulAngle; defm vcmlaq : VectorComplexMLAAngle; } multiclass MVEBinaryVectorHoriz32 { def xsuffix#"q" : Intrinsic (unsignedflag Scalar), subtract, exchange, (zeroinit Scalar32), $a, $b)>; def xsuffix#"q_p" : Intrinsic (unsignedflag Scalar), subtract, exchange, (zeroinit Scalar32), $a, $b, $pred)>; def "a"#xsuffix#"q" : Intrinsic (unsignedflag Scalar), subtract, exchange, $a, $b, $c)>; def "a"#xsuffix#"q_p" : Intrinsic (unsignedflag Scalar), subtract, exchange, $a, $b, $c, $pred)>; } class IntrSplit64 : Intrinsic; class IntrSplit64ZeroInit : Intrinsic; multiclass MVEBinaryVectorHoriz64Base { def xsuffix#"q" : IntrSplit64ZeroInit (unsignedflag Scalar), subtract, exchange, $lo, $hi, $a, $b)>; def xsuffix#"q_p" : IntrSplit64ZeroInit (unsignedflag Scalar), subtract, exchange, $lo, $hi, $a, $b, $pred)>; def "a"#xsuffix#"q" : IntrSplit64 (unsignedflag Scalar), subtract, exchange, $lo, $hi, $b, $c)>; def "a"#xsuffix#"q_p" : IntrSplit64 (unsignedflag Scalar), subtract, exchange, $lo, $hi, $b, $c, $pred)>; } multiclass MVEBinaryVectorHoriz64 { defm "" : MVEBinaryVectorHoriz64Base; } multiclass MVEBinaryVectorHoriz64R { defm "" : MVEBinaryVectorHoriz64Base; } let params = T.Int in { def vabavq : Intrinsic (unsignedflag Scalar), $a, $b, $c)>; def vabavq_p : Intrinsic (unsignedflag Scalar), $a, $b, $c, $pred)>; defm vmladav : MVEBinaryVectorHoriz32; } let params = T.Signed in { defm vmladav : MVEBinaryVectorHoriz32; defm vmlsdav : MVEBinaryVectorHoriz32; defm vmlsdav : MVEBinaryVectorHoriz32; } let params = [u16, s16, u32, s32] in defm vmlaldav : MVEBinaryVectorHoriz64; let params = [s16, s32] in { defm vmlaldav : MVEBinaryVectorHoriz64; defm vmlsldav : MVEBinaryVectorHoriz64; defm vmlsldav : MVEBinaryVectorHoriz64; } let params = T.Int32 in defm vrmlaldavh : MVEBinaryVectorHoriz64R; let params = [s32] in { defm vrmlaldavh : MVEBinaryVectorHoriz64R; defm vrmlsldavh : MVEBinaryVectorHoriz64R; defm vrmlsldavh : MVEBinaryVectorHoriz64R; } foreach desttype = T.All in { // We want a vreinterpretq between every pair of supported vector types // _except_ that there shouldn't be one from a type to itself. // // So this foldl expression implements what you'd write in Python as // [srctype for srctype in T.All if srctype != desttype] let params = !foldl([], T.All, tlist, srctype, !listconcat(tlist, !if(!eq(!cast(desttype),!cast(srctype)),[],[srctype]))) in { def "vreinterpretq_" # desttype: Intrinsic< VecOf, (args Vector:$x), (bitcast $x, VecOf)>; } } let params = T.All in { let pnt = PNT_None in { def vcreateq: Intrinsic), $a, 0), $b, 1), Vector)>; def vuninitializedq: Intrinsic; } // This is the polymorphic form of vuninitializedq, which takes no type // suffix, but takes an _unevaluated_ vector parameter and returns an // uninitialized vector of the same vector type. // // This intrinsic has no _non_-polymorphic form exposed to the user. But each // separately typed version of it still has to have its own clang builtin id, // which can't be called vuninitializedq_u32 or similar because that would // collide with the explicit nullary versions above. So I'm calling them // vuninitializedq_polymorphic_u32 (and so on) for builtin id purposes; that // full name never appears in the header file due to the polymorphicOnly // flag, and the _polymorphic suffix is omitted from the shortened name by // the custom PolymorphicNameType here. let polymorphicOnly = 1, nonEvaluating = 1, pnt = PolymorphicNameType<1, "polymorphic"> in { def vuninitializedq_polymorphic: Intrinsic< Vector, (args Vector), (undef Vector)>; } def vgetq_lane: Intrinsic; def vsetq_lane: Intrinsic:$e, Vector:$v, imm_lane:$lane), (ielt_var $v, $e, $lane)>; }