Revert 3f91705ca5 "ARM-NEON: make type modifiers orthogonal and allow multiple modifiers."

This broke the vcreate_u64 intrinsic. Example:

  $ cat /tmp/a.cc
  #include <arm_neon.h>

  void g() {
    auto v = vcreate_u64(0);
  }
  $ bin/clang -c /tmp/a.cc --target=arm-linux-androideabi16 -march=armv7-a
  /tmp/a.cc:4:12: error: C-style cast from scalar 'int' to vector 'uint64x1_t' (vector of 1 'uint64_t' value) of different size
    auto v = vcreate_u64(0);
             ^~~~~~~~~~~~~~
  /work/llvm.monorepo/build.release/lib/clang/10.0.0/include/arm_neon.h:4144:11: note: expanded from macro 'vcreate_u64'
    __ret = (uint64x1_t)(__p0); \
            ^~~~~~~~~~~~~~~~~~

Reverting until this can be investigated.

> The modifier system used to mutate types on NEON intrinsic definitions had a
> separate letter for all kinds of transformations that might be needed, and we
> were quite quickly running out of letters to use. This patch converts to a much
> smaller set of orthogonal modifiers that can be applied together to achieve the
> desired effect.
>
> When merging with downstream it is likely to cause a conflict with any local
> modifications to the .td files. There is a new script in
> utils/convert_arm_neon.py that was used to convert all .td definitions and I
> would suggest running it on the last downstream version of those files before
> this commit rather than resolving conflicts manually.
This commit is contained in:
Hans Wennborg 2019-11-25 16:27:53 +01:00
parent 357bd914a1
commit 21f26470e9
7 changed files with 1132 additions and 1163 deletions

View File

@ -17,118 +17,118 @@ include "arm_neon_incl.td"
let ArchGuard = "defined(__ARM_FEATURE_FP16_SCALAR_ARITHMETIC) && defined(__aarch64__)" in { let ArchGuard = "defined(__ARM_FEATURE_FP16_SCALAR_ARITHMETIC) && defined(__aarch64__)" in {
// Negate // Negate
def VNEGSH : SInst<"vneg", "11", "Sh">; def VNEGSH : SInst<"vneg", "ss", "Sh">;
// Reciprocal/Sqrt // Reciprocal/Sqrt
def SCALAR_FRECPSH : IInst<"vrecps", "111", "Sh">; def SCALAR_FRECPSH : IInst<"vrecps", "sss", "Sh">;
def FSQRTSH : SInst<"vsqrt", "11", "Sh">; def FSQRTSH : SInst<"vsqrt", "ss", "Sh">;
def SCALAR_FRSQRTSH : IInst<"vrsqrts", "111", "Sh">; def SCALAR_FRSQRTSH : IInst<"vrsqrts", "sss", "Sh">;
// Reciprocal Estimate // Reciprocal Estimate
def SCALAR_FRECPEH : IInst<"vrecpe", "11", "Sh">; def SCALAR_FRECPEH : IInst<"vrecpe", "ss", "Sh">;
// Reciprocal Exponent // Reciprocal Exponent
def SCALAR_FRECPXH : IInst<"vrecpx", "11", "Sh">; def SCALAR_FRECPXH : IInst<"vrecpx", "ss", "Sh">;
// Reciprocal Square Root Estimate // Reciprocal Square Root Estimate
def SCALAR_FRSQRTEH : IInst<"vrsqrte", "11", "Sh">; def SCALAR_FRSQRTEH : IInst<"vrsqrte", "ss", "Sh">;
// Rounding // Rounding
def FRINTZ_S64H : SInst<"vrnd", "11", "Sh">; def FRINTZ_S64H : SInst<"vrnd", "ss", "Sh">;
def FRINTA_S64H : SInst<"vrnda", "11", "Sh">; def FRINTA_S64H : SInst<"vrnda", "ss", "Sh">;
def FRINTI_S64H : SInst<"vrndi", "11", "Sh">; def FRINTI_S64H : SInst<"vrndi", "ss", "Sh">;
def FRINTM_S64H : SInst<"vrndm", "11", "Sh">; def FRINTM_S64H : SInst<"vrndm", "ss", "Sh">;
def FRINTN_S64H : SInst<"vrndn", "11", "Sh">; def FRINTN_S64H : SInst<"vrndn", "ss", "Sh">;
def FRINTP_S64H : SInst<"vrndp", "11", "Sh">; def FRINTP_S64H : SInst<"vrndp", "ss", "Sh">;
def FRINTX_S64H : SInst<"vrndx", "11", "Sh">; def FRINTX_S64H : SInst<"vrndx", "ss", "Sh">;
// Conversion // Conversion
def SCALAR_SCVTFSH : SInst<"vcvth_f16", "(1F)(1!)", "sUs">; def SCALAR_SCVTFSH : SInst<"vcvth_f16", "Ys", "sUs">;
def SCALAR_SCVTFSH1 : SInst<"vcvth_f16", "(1F<)(1!)", "iUi">; def SCALAR_SCVTFSH1 : SInst<"vcvth_f16", "Ys", "iUi">;
def SCALAR_SCVTFSH2 : SInst<"vcvth_f16", "(1F<<)(1!)", "lUl">; def SCALAR_SCVTFSH2 : SInst<"vcvth_f16", "Ys", "lUl">;
def SCALAR_FCVTZSH : SInst<"vcvt_s16", "(1S)1", "Sh">; def SCALAR_FCVTZSH : SInst<"vcvt_s16", "$s", "Sh">;
def SCALAR_FCVTZSH1 : SInst<"vcvt_s32", "(1S>)1", "Sh">; def SCALAR_FCVTZSH1 : SInst<"vcvt_s32", "Is", "Sh">;
def SCALAR_FCVTZSH2 : SInst<"vcvt_s64", "(1S>>)1", "Sh">; def SCALAR_FCVTZSH2 : SInst<"vcvt_s64", "Ls", "Sh">;
def SCALAR_FCVTZUH : SInst<"vcvt_u16", "(1U)1", "Sh">; def SCALAR_FCVTZUH : SInst<"vcvt_u16", "bs", "Sh">;
def SCALAR_FCVTZUH1 : SInst<"vcvt_u32", "(1U>)1", "Sh">; def SCALAR_FCVTZUH1 : SInst<"vcvt_u32", "Us", "Sh">;
def SCALAR_FCVTZUH2 : SInst<"vcvt_u64", "(1U>>)1", "Sh">; def SCALAR_FCVTZUH2 : SInst<"vcvt_u64", "Os", "Sh">;
def SCALAR_FCVTASH : SInst<"vcvta_s16", "(1S)1", "Sh">; def SCALAR_FCVTASH : SInst<"vcvta_s16", "$s", "Sh">;
def SCALAR_FCVTASH1 : SInst<"vcvta_s32", "(1S>)1", "Sh">; def SCALAR_FCVTASH1 : SInst<"vcvta_s32", "Is", "Sh">;
def SCALAR_FCVTASH2 : SInst<"vcvta_s64", "(1S>>)1", "Sh">; def SCALAR_FCVTASH2 : SInst<"vcvta_s64", "Ls", "Sh">;
def SCALAR_FCVTAUH : SInst<"vcvta_u16", "(1U)1", "Sh">; def SCALAR_FCVTAUH : SInst<"vcvta_u16", "bs", "Sh">;
def SCALAR_FCVTAUH1 : SInst<"vcvta_u32", "(1U>)1", "Sh">; def SCALAR_FCVTAUH1 : SInst<"vcvta_u32", "Us", "Sh">;
def SCALAR_FCVTAUH2 : SInst<"vcvta_u64", "(1U>>)1", "Sh">; def SCALAR_FCVTAUH2 : SInst<"vcvta_u64", "Os", "Sh">;
def SCALAR_FCVTMSH : SInst<"vcvtm_s16", "(1S)1", "Sh">; def SCALAR_FCVTMSH : SInst<"vcvtm_s16", "$s", "Sh">;
def SCALAR_FCVTMSH1 : SInst<"vcvtm_s32", "(1S>)1", "Sh">; def SCALAR_FCVTMSH1 : SInst<"vcvtm_s32", "Is", "Sh">;
def SCALAR_FCVTMSH2 : SInst<"vcvtm_s64", "(1S>>)1", "Sh">; def SCALAR_FCVTMSH2 : SInst<"vcvtm_s64", "Ls", "Sh">;
def SCALAR_FCVTMUH : SInst<"vcvtm_u16", "(1U)1", "Sh">; def SCALAR_FCVTMUH : SInst<"vcvtm_u16", "bs", "Sh">;
def SCALAR_FCVTMUH1 : SInst<"vcvtm_u32", "(1U>)1", "Sh">; def SCALAR_FCVTMUH1 : SInst<"vcvtm_u32", "Us", "Sh">;
def SCALAR_FCVTMUH2 : SInst<"vcvtm_u64", "(1U>>)1", "Sh">; def SCALAR_FCVTMUH2 : SInst<"vcvtm_u64", "Os", "Sh">;
def SCALAR_FCVTNSH : SInst<"vcvtn_s16", "(1S)1", "Sh">; def SCALAR_FCVTNSH : SInst<"vcvtn_s16", "$s", "Sh">;
def SCALAR_FCVTNSH1 : SInst<"vcvtn_s32", "(1S>)1", "Sh">; def SCALAR_FCVTNSH1 : SInst<"vcvtn_s32", "Is", "Sh">;
def SCALAR_FCVTNSH2 : SInst<"vcvtn_s64", "(1S>>)1", "Sh">; def SCALAR_FCVTNSH2 : SInst<"vcvtn_s64", "Ls", "Sh">;
def SCALAR_FCVTNUH : SInst<"vcvtn_u16", "(1U)1", "Sh">; def SCALAR_FCVTNUH : SInst<"vcvtn_u16", "bs", "Sh">;
def SCALAR_FCVTNUH1 : SInst<"vcvtn_u32", "(1U>)1", "Sh">; def SCALAR_FCVTNUH1 : SInst<"vcvtn_u32", "Us", "Sh">;
def SCALAR_FCVTNUH2 : SInst<"vcvtn_u64", "(1U>>)1", "Sh">; def SCALAR_FCVTNUH2 : SInst<"vcvtn_u64", "Os", "Sh">;
def SCALAR_FCVTPSH : SInst<"vcvtp_s16", "(1S)1", "Sh">; def SCALAR_FCVTPSH : SInst<"vcvtp_s16", "$s", "Sh">;
def SCALAR_FCVTPSH1 : SInst<"vcvtp_s32", "(1S>)1", "Sh">; def SCALAR_FCVTPSH1 : SInst<"vcvtp_s32", "Is", "Sh">;
def SCALAR_FCVTPSH2 : SInst<"vcvtp_s64", "(1S>>)1", "Sh">; def SCALAR_FCVTPSH2 : SInst<"vcvtp_s64", "Ls", "Sh">;
def SCALAR_FCVTPUH : SInst<"vcvtp_u16", "(1U)1", "Sh">; def SCALAR_FCVTPUH : SInst<"vcvtp_u16", "bs", "Sh">;
def SCALAR_FCVTPUH1 : SInst<"vcvtp_u32", "(1U>)1", "Sh">; def SCALAR_FCVTPUH1 : SInst<"vcvtp_u32", "Us", "Sh">;
def SCALAR_FCVTPUH2 : SInst<"vcvtp_u64", "(1U>>)1", "Sh">; def SCALAR_FCVTPUH2 : SInst<"vcvtp_u64", "Os", "Sh">;
let isVCVT_N = 1 in { let isVCVT_N = 1 in {
def SCALAR_SCVTFSHO : SInst<"vcvth_n_f16", "(1F)(1!)I", "sUs">; def SCALAR_SCVTFSHO : SInst<"vcvth_n_f16", "Ysi", "sUs">;
def SCALAR_SCVTFSH1O: SInst<"vcvth_n_f16", "(1F<)(1!)I", "iUi">; def SCALAR_SCVTFSH1O: SInst<"vcvth_n_f16", "Ysi", "iUi">;
def SCALAR_SCVTFSH2O: SInst<"vcvth_n_f16", "(1F<<)(1!)I", "lUl">; def SCALAR_SCVTFSH2O: SInst<"vcvth_n_f16", "Ysi", "lUl">;
def SCALAR_FCVTZSHO : SInst<"vcvt_n_s16", "(1S)1I", "Sh">; def SCALAR_FCVTZSHO : SInst<"vcvt_n_s16", "$si", "Sh">;
def SCALAR_FCVTZSH1O: SInst<"vcvt_n_s32", "(1S>)1I", "Sh">; def SCALAR_FCVTZSH1O: SInst<"vcvt_n_s32", "Isi", "Sh">;
def SCALAR_FCVTZSH2O: SInst<"vcvt_n_s64", "(1S>>)1I", "Sh">; def SCALAR_FCVTZSH2O: SInst<"vcvt_n_s64", "Lsi", "Sh">;
def SCALAR_FCVTZUHO : SInst<"vcvt_n_u16", "(1U)1I", "Sh">; def SCALAR_FCVTZUHO : SInst<"vcvt_n_u16", "bsi", "Sh">;
def SCALAR_FCVTZUH1O: SInst<"vcvt_n_u32", "(1U>)1I", "Sh">; def SCALAR_FCVTZUH1O: SInst<"vcvt_n_u32", "Usi", "Sh">;
def SCALAR_FCVTZUH2O: SInst<"vcvt_n_u64", "(1U>>)1I", "Sh">; def SCALAR_FCVTZUH2O: SInst<"vcvt_n_u64", "Osi", "Sh">;
} }
// Comparison // Comparison
def SCALAR_CMEQRH : SInst<"vceq", "(1U)11", "Sh">; def SCALAR_CMEQRH : SInst<"vceq", "bss", "Sh">;
def SCALAR_CMEQZH : SInst<"vceqz", "(1U)1", "Sh">; def SCALAR_CMEQZH : SInst<"vceqz", "bs", "Sh">;
def SCALAR_CMGERH : SInst<"vcge", "(1U)11", "Sh">; def SCALAR_CMGERH : SInst<"vcge", "bss", "Sh">;
def SCALAR_CMGEZH : SInst<"vcgez", "(1U)1", "Sh">; def SCALAR_CMGEZH : SInst<"vcgez", "bs", "Sh">;
def SCALAR_CMGTRH : SInst<"vcgt", "(1U)11", "Sh">; def SCALAR_CMGTRH : SInst<"vcgt", "bss", "Sh">;
def SCALAR_CMGTZH : SInst<"vcgtz", "(1U)1", "Sh">; def SCALAR_CMGTZH : SInst<"vcgtz", "bs", "Sh">;
def SCALAR_CMLERH : SInst<"vcle", "(1U)11", "Sh">; def SCALAR_CMLERH : SInst<"vcle", "bss", "Sh">;
def SCALAR_CMLEZH : SInst<"vclez", "(1U)1", "Sh">; def SCALAR_CMLEZH : SInst<"vclez", "bs", "Sh">;
def SCALAR_CMLTH : SInst<"vclt", "(1U)11", "Sh">; def SCALAR_CMLTH : SInst<"vclt", "bss", "Sh">;
def SCALAR_CMLTZH : SInst<"vcltz", "(1U)1", "Sh">; def SCALAR_CMLTZH : SInst<"vcltz", "bs", "Sh">;
// Absolute Compare Mask Greater Than Or Equal // Absolute Compare Mask Greater Than Or Equal
def SCALAR_FACGEH : IInst<"vcage", "(1U)11", "Sh">; def SCALAR_FACGEH : IInst<"vcage", "bss", "Sh">;
def SCALAR_FACLEH : IInst<"vcale", "(1U)11", "Sh">; def SCALAR_FACLEH : IInst<"vcale", "bss", "Sh">;
// Absolute Compare Mask Greater Than // Absolute Compare Mask Greater Than
def SCALAR_FACGT : IInst<"vcagt", "(1U)11", "Sh">; def SCALAR_FACGT : IInst<"vcagt", "bss", "Sh">;
def SCALAR_FACLT : IInst<"vcalt", "(1U)11", "Sh">; def SCALAR_FACLT : IInst<"vcalt", "bss", "Sh">;
// Scalar Absolute Value // Scalar Absolute Value
def SCALAR_ABSH : SInst<"vabs", "11", "Sh">; def SCALAR_ABSH : SInst<"vabs", "ss", "Sh">;
// Scalar Absolute Difference // Scalar Absolute Difference
def SCALAR_ABDH: IInst<"vabd", "111", "Sh">; def SCALAR_ABDH: IInst<"vabd", "sss", "Sh">;
// Add/Sub // Add/Sub
def VADDSH : SInst<"vadd", "111", "Sh">; def VADDSH : SInst<"vadd", "sss", "Sh">;
def VSUBHS : SInst<"vsub", "111", "Sh">; def VSUBHS : SInst<"vsub", "sss", "Sh">;
// Max/Min // Max/Min
def VMAXHS : SInst<"vmax", "111", "Sh">; def VMAXHS : SInst<"vmax", "sss", "Sh">;
def VMINHS : SInst<"vmin", "111", "Sh">; def VMINHS : SInst<"vmin", "sss", "Sh">;
def FMAXNMHS : SInst<"vmaxnm", "111", "Sh">; def FMAXNMHS : SInst<"vmaxnm", "sss", "Sh">;
def FMINNMHS : SInst<"vminnm", "111", "Sh">; def FMINNMHS : SInst<"vminnm", "sss", "Sh">;
// Multiplication/Division // Multiplication/Division
def VMULHS : SInst<"vmul", "111", "Sh">; def VMULHS : SInst<"vmul", "sss", "Sh">;
def MULXHS : SInst<"vmulx", "111", "Sh">; def MULXHS : SInst<"vmulx", "sss", "Sh">;
def FDIVHS : SInst<"vdiv", "111", "Sh">; def FDIVHS : SInst<"vdiv", "sss", "Sh">;
// Vector fused multiply-add operations // Vector fused multiply-add operations
def VFMAHS : SInst<"vfma", "1111", "Sh">; def VFMAHS : SInst<"vfma", "ssss", "Sh">;
def VFMSHS : SInst<"vfms", "1111", "Sh">; def VFMSHS : SInst<"vfms", "ssss", "Sh">;
} }

File diff suppressed because it is too large Load Diff

View File

@ -198,8 +198,10 @@ def OP_UNAVAILABLE : Operation {
// //
// The prototype is a string that defines the return type of the intrinsic // The prototype is a string that defines the return type of the intrinsic
// and the type of each argument. The return type and every argument gets a // and the type of each argument. The return type and every argument gets a
// set of "modifiers" that can change in some way the "base type" of the // "modifier" that can change in some way the "base type" of the intrinsic.
// intrinsic. //
// The modifier 'd' means "default" and does not modify the base type in any
// way. The available modifiers are given below.
// //
// Typespecs // Typespecs
// --------- // ---------
@ -224,34 +226,41 @@ def OP_UNAVAILABLE : Operation {
// ------------------- // -------------------
// prototype: return (arg, arg, ...) // prototype: return (arg, arg, ...)
// //
// Each type modifier is either a single character, or a group surrounded by // v: void
// parentheses. // t: best-fit integer (int/poly args)
// // x: signed integer (int/float args)
// .: default // u: unsigned integer (int/float args)
// v: change to void category. // f: float (int args)
// S: change to signed integer category. // F: double (int args)
// U: change to unsigned integer category. // H: half (int args)
// F: change to floating category. // 0: half (int args), ignore 'Q' size modifier.
// P: change to polynomial category. // 1: half (int args), force 'Q' size modifier.
// p: change polynomial to equivalent integer category. Otherwise nop. // d: default
// // g: default, ignore 'Q' size modifier.
// >: double element width (vector size unchanged). // j: default, force 'Q' size modifier.
// <: half element width (vector size unchanged). // w: double width elements, same num elts
// // n: double width elements, half num elts
// 1: change to scalar. // h: half width elements, double num elts
// 2: change to struct of two vectors. // q: half width elements, quad num elts
// 3: change to struct of three vectors. // e: half width elements, double num elts, unsigned
// 4: change to struct of four vectors. // m: half width elements, same num elts
// // i: constant int
// *: make a pointer argument. // l: constant uint64
// c: make a constant argument (for pointers). // s: scalar of element type
// // z: scalar of half width element type, signed
// Q: force 128-bit width. // r: scalar of double width element type, signed
// q: force 64-bit width. // b: scalar of unsigned integer/long type (int/float args)
// // $: scalar of signed integer/long type (int/float args)
// I: make 32-bit signed scalar immediate // y: scalar of float
// !: make this the key type passed to CGBuiltin.cpp in a polymorphic call. // o: scalar of double
// k: default elt width, double num elts
// 2,3,4: array of default vectors
// B,C,D: array of default elts, force 'Q' size modifier.
// p: pointer type
// c: const pointer type
// 7: vector of 8-bit elements, ignore 'Q' size modifier
// 8: vector of 8-bit elements, same width as default type
// 9: vector of 8-bit elements, force 'Q' size modifier
// Every intrinsic subclasses Inst. // Every intrinsic subclasses Inst.
class Inst <string n, string p, string t, Operation o> { class Inst <string n, string p, string t, Operation o> {

View File

@ -17756,6 +17756,8 @@ float32_t test_vminnmv_f32(float32x2_t a) {
} }
// CHECK-LABEL: @test_vpaddq_s64( // CHECK-LABEL: @test_vpaddq_s64(
// CHECK: [[TMP0:%.*]] = bitcast <2 x i64> %a to <16 x i8>
// CHECK: [[TMP1:%.*]] = bitcast <2 x i64> %b to <16 x i8>
// CHECK: [[VPADDQ_V2_I:%.*]] = call <2 x i64> @llvm.aarch64.neon.addp.v2i64(<2 x i64> %a, <2 x i64> %b) // CHECK: [[VPADDQ_V2_I:%.*]] = call <2 x i64> @llvm.aarch64.neon.addp.v2i64(<2 x i64> %a, <2 x i64> %b)
// CHECK: [[VPADDQ_V3_I:%.*]] = bitcast <2 x i64> [[VPADDQ_V2_I]] to <16 x i8> // CHECK: [[VPADDQ_V3_I:%.*]] = bitcast <2 x i64> [[VPADDQ_V2_I]] to <16 x i8>
// CHECK: ret <2 x i64> [[VPADDQ_V2_I]] // CHECK: ret <2 x i64> [[VPADDQ_V2_I]]
@ -17764,6 +17766,8 @@ int64x2_t test_vpaddq_s64(int64x2_t a, int64x2_t b) {
} }
// CHECK-LABEL: @test_vpaddq_u64( // CHECK-LABEL: @test_vpaddq_u64(
// CHECK: [[TMP0:%.*]] = bitcast <2 x i64> %a to <16 x i8>
// CHECK: [[TMP1:%.*]] = bitcast <2 x i64> %b to <16 x i8>
// CHECK: [[VPADDQ_V2_I:%.*]] = call <2 x i64> @llvm.aarch64.neon.addp.v2i64(<2 x i64> %a, <2 x i64> %b) // CHECK: [[VPADDQ_V2_I:%.*]] = call <2 x i64> @llvm.aarch64.neon.addp.v2i64(<2 x i64> %a, <2 x i64> %b)
// CHECK: [[VPADDQ_V3_I:%.*]] = bitcast <2 x i64> [[VPADDQ_V2_I]] to <16 x i8> // CHECK: [[VPADDQ_V3_I:%.*]] = bitcast <2 x i64> [[VPADDQ_V2_I]] to <16 x i8>
// CHECK: ret <2 x i64> [[VPADDQ_V2_I]] // CHECK: ret <2 x i64> [[VPADDQ_V2_I]]

View File

@ -407,10 +407,12 @@ int64_t test_vqdmlsls_laneq_s32(int64_t a, int32_t b, int32x4_t c) {
} }
// CHECK-LABEL: define <1 x double> @test_vmulx_lane_f64_0() #0 { // CHECK-LABEL: define <1 x double> @test_vmulx_lane_f64_0() #0 {
// CHECK: [[VGET_LANE:%.*]] = extractelement <1 x double> <double 0x3FD6304BC43AB5C2>, i32 0 // CHECK: [[TMP0:%.*]] = bitcast i64 4599917171378402754 to <1 x double>
// CHECK: [[VGET_LANE7:%.*]] = extractelement <1 x double> <double 0x3FEE211E215AEEF3>, i32 0 // CHECK: [[TMP1:%.*]] = bitcast i64 4606655882138939123 to <1 x double>
// CHECK: [[VGET_LANE:%.*]] = extractelement <1 x double> [[TMP0]], i32 0
// CHECK: [[VGET_LANE7:%.*]] = extractelement <1 x double> [[TMP1]], i32 0
// CHECK: [[VMULXD_F64_I:%.*]] = call double @llvm.aarch64.neon.fmulx.f64(double [[VGET_LANE]], double [[VGET_LANE7]]) // CHECK: [[VMULXD_F64_I:%.*]] = call double @llvm.aarch64.neon.fmulx.f64(double [[VGET_LANE]], double [[VGET_LANE7]])
// CHECK: [[VSET_LANE:%.*]] = insertelement <1 x double> <double 0x3FD6304BC43AB5C2>, double [[VMULXD_F64_I]], i32 0 // CHECK: [[VSET_LANE:%.*]] = insertelement <1 x double> [[TMP0]], double [[VMULXD_F64_I]], i32 0
// CHECK: ret <1 x double> [[VSET_LANE]] // CHECK: ret <1 x double> [[VSET_LANE]]
float64x1_t test_vmulx_lane_f64_0() { float64x1_t test_vmulx_lane_f64_0() {
float64x1_t arg1; float64x1_t arg1;
@ -424,11 +426,13 @@ float64x1_t test_vmulx_lane_f64_0() {
} }
// CHECK-LABEL: define <1 x double> @test_vmulx_laneq_f64_2() #1 { // CHECK-LABEL: define <1 x double> @test_vmulx_laneq_f64_2() #1 {
// CHECK: [[SHUFFLE_I:%.*]] = shufflevector <1 x double> <double 0x3FD6304BC43AB5C2>, <1 x double> <double 0x3FEE211E215AEEF3>, <2 x i32> <i32 0, i32 1> // CHECK: [[TMP0:%.*]] = bitcast i64 4599917171378402754 to <1 x double>
// CHECK: [[VGET_LANE:%.*]] = extractelement <1 x double> <double 0x3FD6304BC43AB5C2>, i32 0 // CHECK: [[TMP1:%.*]] = bitcast i64 4606655882138939123 to <1 x double>
// CHECK: [[SHUFFLE_I:%.*]] = shufflevector <1 x double> [[TMP0]], <1 x double> [[TMP1]], <2 x i32> <i32 0, i32 1>
// CHECK: [[VGET_LANE:%.*]] = extractelement <1 x double> [[TMP0]], i32 0
// CHECK: [[VGETQ_LANE:%.*]] = extractelement <2 x double> [[SHUFFLE_I]], i32 1 // CHECK: [[VGETQ_LANE:%.*]] = extractelement <2 x double> [[SHUFFLE_I]], i32 1
// CHECK: [[VMULXD_F64_I:%.*]] = call double @llvm.aarch64.neon.fmulx.f64(double [[VGET_LANE]], double [[VGETQ_LANE]]) // CHECK: [[VMULXD_F64_I:%.*]] = call double @llvm.aarch64.neon.fmulx.f64(double [[VGET_LANE]], double [[VGETQ_LANE]])
// CHECK: [[VSET_LANE:%.*]] = insertelement <1 x double> <double 0x3FD6304BC43AB5C2>, double [[VMULXD_F64_I]], i32 0 // CHECK: [[VSET_LANE:%.*]] = insertelement <1 x double> [[TMP0]], double [[VMULXD_F64_I]], i32 0
// CHECK: ret <1 x double> [[VSET_LANE]] // CHECK: ret <1 x double> [[VSET_LANE]]
float64x1_t test_vmulx_laneq_f64_2() { float64x1_t test_vmulx_laneq_f64_2() {
float64x1_t arg1; float64x1_t arg1;

View File

@ -161,11 +161,11 @@ public:
Pointer(false), ScalarForMangling(false), NoManglingQ(false), Pointer(false), ScalarForMangling(false), NoManglingQ(false),
Bitwidth(0), ElementBitwidth(0), NumVectors(0) {} Bitwidth(0), ElementBitwidth(0), NumVectors(0) {}
Type(TypeSpec TS, StringRef CharMods) Type(TypeSpec TS, char CharMod)
: TS(std::move(TS)), Kind(Void), Immediate(false), : TS(std::move(TS)), Kind(Void), Immediate(false),
Constant(false), Pointer(false), ScalarForMangling(false), Constant(false), Pointer(false), ScalarForMangling(false),
NoManglingQ(false), Bitwidth(0), ElementBitwidth(0), NumVectors(0) { NoManglingQ(false), Bitwidth(0), ElementBitwidth(0), NumVectors(0) {
applyModifiers(CharMods); applyModifier(CharMod);
} }
/// Returns a type representing "void". /// Returns a type representing "void".
@ -181,15 +181,13 @@ public:
bool noManglingQ() const { return NoManglingQ; } bool noManglingQ() const { return NoManglingQ; }
bool isPointer() const { return Pointer; } bool isPointer() const { return Pointer; }
bool isValue() const { return !isVoid() && !isPointer(); }
bool isScalar() const { return isValue() && NumVectors == 0; }
bool isVector() const { return isValue() && NumVectors > 0; }
bool isConstPointer() const { return Constant; }
bool isFloating() const { return Kind == Float; } bool isFloating() const { return Kind == Float; }
bool isInteger() const { return Kind == SInt || Kind == UInt; } bool isInteger() const { return Kind == SInt || Kind == UInt; }
bool isPoly() const { return Kind == Poly; } bool isPoly() const { return Kind == Poly; }
bool isSigned() const { return Kind == SInt; } bool isSigned() const { return Kind == SInt; }
bool isImmediate() const { return Immediate; } bool isImmediate() const { return Immediate; }
bool isScalar() const { return NumVectors == 0; }
bool isVector() const { return NumVectors > 0; }
bool isFloat() const { return isFloating() && ElementBitwidth == 32; } bool isFloat() const { return isFloating() && ElementBitwidth == 32; }
bool isDouble() const { return isFloating() && ElementBitwidth == 64; } bool isDouble() const { return isFloating() && ElementBitwidth == 64; }
bool isHalf() const { return isFloating() && ElementBitwidth == 16; } bool isHalf() const { return isFloating() && ElementBitwidth == 16; }
@ -207,11 +205,11 @@ public:
// Mutator functions // Mutator functions
// //
void makeUnsigned() { void makeUnsigned() {
assert(!isVoid() && "not a potentially signed type"); assert(isInteger() && "not a potentially signed type");
Kind = UInt; Kind = UInt;
} }
void makeSigned() { void makeSigned() {
assert(!isVoid() && "not a potentially signed type"); assert(isInteger() && "not a potentially signed type");
Kind = SInt; Kind = SInt;
} }
@ -269,8 +267,8 @@ private:
/// seen. This is needed by applyModifier as some modifiers /// seen. This is needed by applyModifier as some modifiers
/// only take effect if the type size was changed by "Q" or "H". /// only take effect if the type size was changed by "Q" or "H".
void applyTypespec(bool &Quad); void applyTypespec(bool &Quad);
/// Applies prototype modifiers to the type. /// Applies a prototype modifiers to the type.
void applyModifiers(StringRef Mods); void applyModifier(char Mod);
}; };
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -301,8 +299,8 @@ class Intrinsic {
/// The Record this intrinsic was created from. /// The Record this intrinsic was created from.
Record *R; Record *R;
/// The unmangled name. /// The unmangled name and prototype.
std::string Name; std::string Name, Proto;
/// The input and output typespecs. InTS == OutTS except when /// The input and output typespecs. InTS == OutTS except when
/// CartesianProductOfTypes is 1 - this is the case for vreinterpret. /// CartesianProductOfTypes is 1 - this is the case for vreinterpret.
TypeSpec OutTS, InTS; TypeSpec OutTS, InTS;
@ -325,8 +323,6 @@ class Intrinsic {
/// The types of return value [0] and parameters [1..]. /// The types of return value [0] and parameters [1..].
std::vector<Type> Types; std::vector<Type> Types;
/// The index of the key type passed to CGBuiltin.cpp for polymorphic calls.
int PolymorphicKeyType;
/// The local variables defined. /// The local variables defined.
std::map<std::string, Variable> Variables; std::map<std::string, Variable> Variables;
/// NeededEarly - set if any other intrinsic depends on this intrinsic. /// NeededEarly - set if any other intrinsic depends on this intrinsic.
@ -362,39 +358,34 @@ public:
Intrinsic(Record *R, StringRef Name, StringRef Proto, TypeSpec OutTS, Intrinsic(Record *R, StringRef Name, StringRef Proto, TypeSpec OutTS,
TypeSpec InTS, ClassKind CK, ListInit *Body, NeonEmitter &Emitter, TypeSpec InTS, ClassKind CK, ListInit *Body, NeonEmitter &Emitter,
StringRef Guard, bool IsUnavailable, bool BigEndianSafe) StringRef Guard, bool IsUnavailable, bool BigEndianSafe)
: R(R), Name(Name.str()), OutTS(OutTS), InTS(InTS), CK(CK), Body(Body), : R(R), Name(Name.str()), Proto(Proto.str()), OutTS(OutTS), InTS(InTS),
Guard(Guard.str()), IsUnavailable(IsUnavailable), CK(CK), Body(Body), Guard(Guard.str()), IsUnavailable(IsUnavailable),
BigEndianSafe(BigEndianSafe), PolymorphicKeyType(0), NeededEarly(false), BigEndianSafe(BigEndianSafe), NeededEarly(false), UseMacro(false),
UseMacro(false), BaseType(OutTS, "."), InBaseType(InTS, "."), BaseType(OutTS, 'd'), InBaseType(InTS, 'd'), Emitter(Emitter) {
Emitter(Emitter) { // If this builtin takes an immediate argument, we need to #define it rather
// than use a standard declaration, so that SemaChecking can range check
// the immediate passed by the user.
if (Proto.find('i') != std::string::npos)
UseMacro = true;
// Pointer arguments need to use macros to avoid hiding aligned attributes
// from the pointer type.
if (Proto.find('p') != std::string::npos ||
Proto.find('c') != std::string::npos)
UseMacro = true;
// It is not permitted to pass or return an __fp16 by value, so intrinsics
// taking a scalar float16_t must be implemented as macros.
if (OutTS.find('h') != std::string::npos &&
Proto.find('s') != std::string::npos)
UseMacro = true;
// Modify the TypeSpec per-argument to get a concrete Type, and create // Modify the TypeSpec per-argument to get a concrete Type, and create
// known variables for each. // known variables for each.
// Types[0] is the return value. // Types[0] is the return value.
unsigned Pos = 0; Types.emplace_back(OutTS, Proto[0]);
Types.emplace_back(OutTS, getNextModifiers(Proto, Pos)); for (unsigned I = 1; I < Proto.size(); ++I)
StringRef Mods = getNextModifiers(Proto, Pos); Types.emplace_back(InTS, Proto[I]);
while (!Mods.empty()) {
Types.emplace_back(InTS, Mods);
if (Mods.find("!") != StringRef::npos)
PolymorphicKeyType = Types.size() - 1;
Mods = getNextModifiers(Proto, Pos);
}
for (auto Type : Types) {
// If this builtin takes an immediate argument, we need to #define it rather
// than use a standard declaration, so that SemaChecking can range check
// the immediate passed by the user.
// Pointer arguments need to use macros to avoid hiding aligned attributes
// from the pointer type.
// It is not permitted to pass or return an __fp16 by value, so intrinsics
// taking a scalar float16_t must be implemented as macros.
if (Type.isImmediate() || Type.isPointer() ||
(Type.isScalar() && Type.isHalf()))
UseMacro = true;
}
} }
/// Get the Record that this intrinsic is based off. /// Get the Record that this intrinsic is based off.
@ -410,24 +401,23 @@ public:
/// Return true if the intrinsic takes an immediate operand. /// Return true if the intrinsic takes an immediate operand.
bool hasImmediate() const { bool hasImmediate() const {
return std::any_of(Types.begin(), Types.end(), return Proto.find('i') != std::string::npos;
[](const Type &T) { return T.isImmediate(); });
} }
/// Return the parameter index of the immediate operand. /// Return the parameter index of the immediate operand.
unsigned getImmediateIdx() const { unsigned getImmediateIdx() const {
for (unsigned Idx = 0; Idx < Types.size(); ++Idx) assert(hasImmediate());
if (Types[Idx].isImmediate()) unsigned Idx = Proto.find('i');
return Idx - 1; assert(Idx > 0 && "Can't return an immediate!");
llvm_unreachable("Intrinsic has no immediate"); return Idx - 1;
} }
unsigned getNumParams() const { return Proto.size() - 1; }
unsigned getNumParams() const { return Types.size() - 1; }
Type getReturnType() const { return Types[0]; } Type getReturnType() const { return Types[0]; }
Type getParamType(unsigned I) const { return Types[I + 1]; } Type getParamType(unsigned I) const { return Types[I + 1]; }
Type getBaseType() const { return BaseType; } Type getBaseType() const { return BaseType; }
Type getPolymorphicKeyType() const { return Types[PolymorphicKeyType]; } /// Return the raw prototype string.
std::string getProto() const { return Proto; }
/// Return true if the prototype has a scalar argument. /// Return true if the prototype has a scalar argument.
bool protoHasScalar() const; bool protoHasScalar() const;
@ -481,8 +471,6 @@ public:
void indexBody(); void indexBody();
private: private:
StringRef getNextModifiers(StringRef Proto, unsigned &Pos) const;
std::string mangleName(std::string Name, ClassKind CK) const; std::string mangleName(std::string Name, ClassKind CK) const;
void initVariables(); void initVariables();
@ -626,14 +614,10 @@ std::string Type::builtin_str() const {
if (isVoid()) if (isVoid())
return "v"; return "v";
if (isPointer()) { if (Pointer)
// All pointers are void pointers. // All pointers are void pointers.
S = "v"; S += "v";
if (isConstPointer()) else if (isInteger())
S += "C";
S += "*";
return S;
} else if (isInteger())
switch (ElementBitwidth) { switch (ElementBitwidth) {
case 8: S += "c"; break; case 8: S += "c"; break;
case 16: S += "s"; break; case 16: S += "s"; break;
@ -650,11 +634,10 @@ std::string Type::builtin_str() const {
default: llvm_unreachable("Unhandled case!"); default: llvm_unreachable("Unhandled case!");
} }
// FIXME: NECESSARY???????????????????????????????????????????????????????????????????????
if (isChar() && !isPointer() && isSigned()) if (isChar() && !isPointer() && isSigned())
// Make chars explicitly signed. // Make chars explicitly signed.
S = "S" + S; S = "S" + S;
else if (isInteger() && !isSigned()) else if (!isPointer() && isInteger() && !isSigned())
S = "U" + S; S = "U" + S;
// Constant indices are "int", but have the "constant expression" modifier. // Constant indices are "int", but have the "constant expression" modifier.
@ -663,8 +646,11 @@ std::string Type::builtin_str() const {
S = "I" + S; S = "I" + S;
} }
if (isScalar()) if (isScalar()) {
if (Constant) S += "C";
if (Pointer) S += "*";
return S; return S;
}
std::string Ret; std::string Ret;
for (unsigned I = 0; I < NumVectors; ++I) for (unsigned I = 0; I < NumVectors; ++I)
@ -826,77 +812,202 @@ void Type::applyTypespec(bool &Quad) {
Bitwidth = Quad ? 128 : 64; Bitwidth = Quad ? 128 : 64;
} }
void Type::applyModifiers(StringRef Mods) { void Type::applyModifier(char Mod) {
bool AppliedQuad = false; bool AppliedQuad = false;
applyTypespec(AppliedQuad); applyTypespec(AppliedQuad);
for (char Mod : Mods) { switch (Mod) {
switch (Mod) { case 'v':
case '.': Kind = Void;
break; break;
case 'v': case 't':
Kind = Void; if (isPoly())
break;
case 'S':
Kind = SInt;
break;
case 'U':
Kind = UInt; Kind = UInt;
break; break;
case 'F': case 'b':
Kind = Float; Kind = UInt;
break; NumVectors = 0;
case 'P': Bitwidth = ElementBitwidth;
Kind = Poly; break;
break; case '$':
case '>': Kind = SInt;
assert(ElementBitwidth < 128); NumVectors = 0;
ElementBitwidth *= 2; Bitwidth = ElementBitwidth;
break; break;
case '<': case 'u':
assert(ElementBitwidth > 8); Kind = UInt;
ElementBitwidth /= 2; break;
break; case 'x':
case '1': assert(!isPoly() && "'u' can't be used with poly types!");
NumVectors = 0; Kind = SInt;
break; break;
case '2': case 'o':
NumVectors = 2; Bitwidth = ElementBitwidth = 64;
break; NumVectors = 0;
case '3': Kind = Float;
NumVectors = 3; break;
break; case 'y':
case '4': Bitwidth = ElementBitwidth = 32;
NumVectors = 4; NumVectors = 0;
break; Kind = Float;
case '*': break;
Pointer = true; case 'Y':
break; Bitwidth = ElementBitwidth = 16;
case 'c': NumVectors = 0;
Constant = true; Kind = Float;
break; break;
case 'Q': case 'I':
Bitwidth = 128; Bitwidth = ElementBitwidth = 32;
break; NumVectors = 0;
case 'q': Kind = SInt;
Bitwidth = 64; break;
break; case 'L':
case 'I': Bitwidth = ElementBitwidth = 64;
Kind = SInt; NumVectors = 0;
ElementBitwidth = Bitwidth = 32; Kind = SInt;
NumVectors = 0; break;
Immediate = true; case 'U':
break; Bitwidth = ElementBitwidth = 32;
case 'p': NumVectors = 0;
if (isPoly()) Kind = UInt;
Kind = UInt; break;
break; case 'O':
case '!': Bitwidth = ElementBitwidth = 64;
// Key type, handled elsewhere. NumVectors = 0;
break; Kind = UInt;
default: break;
llvm_unreachable("Unhandled character!"); case 'f':
} Kind = Float;
ElementBitwidth = 32;
break;
case 'F':
Kind = Float;
ElementBitwidth = 64;
break;
case 'H':
Kind = Float;
ElementBitwidth = 16;
break;
case '0':
Kind = Float;
if (AppliedQuad)
Bitwidth /= 2;
ElementBitwidth = 16;
break;
case '1':
Kind = Float;
if (!AppliedQuad)
Bitwidth *= 2;
ElementBitwidth = 16;
break;
case 'g':
if (AppliedQuad)
Bitwidth /= 2;
break;
case 'j':
if (!AppliedQuad)
Bitwidth *= 2;
break;
case 'w':
ElementBitwidth *= 2;
Bitwidth *= 2;
break;
case 'n':
ElementBitwidth *= 2;
break;
case 'i':
Kind = SInt;
ElementBitwidth = Bitwidth = 32;
NumVectors = 0;
Immediate = true;
break;
case 'l':
Kind = UInt;
ElementBitwidth = Bitwidth = 64;
NumVectors = 0;
Immediate = true;
break;
case 'z':
ElementBitwidth /= 2;
Bitwidth = ElementBitwidth;
NumVectors = 0;
break;
case 'r':
ElementBitwidth *= 2;
Bitwidth = ElementBitwidth;
NumVectors = 0;
break;
case 's':
Bitwidth = ElementBitwidth;
NumVectors = 0;
break;
case 'k':
Bitwidth *= 2;
break;
case 'c':
Constant = true;
LLVM_FALLTHROUGH;
case 'p':
Pointer = true;
Bitwidth = ElementBitwidth;
NumVectors = 0;
break;
case 'h':
ElementBitwidth /= 2;
break;
case 'q':
ElementBitwidth /= 2;
Bitwidth *= 2;
break;
case 'e':
ElementBitwidth /= 2;
Kind = UInt;
break;
case 'm':
ElementBitwidth /= 2;
Bitwidth /= 2;
break;
case 'd':
break;
case '2':
NumVectors = 2;
break;
case '3':
NumVectors = 3;
break;
case '4':
NumVectors = 4;
break;
case 'B':
NumVectors = 2;
if (!AppliedQuad)
Bitwidth *= 2;
break;
case 'C':
NumVectors = 3;
if (!AppliedQuad)
Bitwidth *= 2;
break;
case 'D':
NumVectors = 4;
if (!AppliedQuad)
Bitwidth *= 2;
break;
case '7':
if (AppliedQuad)
Bitwidth /= 2;
ElementBitwidth = 8;
break;
case '8':
ElementBitwidth = 8;
break;
case '9':
if (!AppliedQuad)
Bitwidth *= 2;
ElementBitwidth = 8;
break;
default:
llvm_unreachable("Unhandled character!");
} }
} }
@ -904,19 +1015,6 @@ void Type::applyModifiers(StringRef Mods) {
// Intrinsic implementation // Intrinsic implementation
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
StringRef Intrinsic::getNextModifiers(StringRef Proto, unsigned &Pos) const {
if (Proto.size() == Pos)
return StringRef();
else if (Proto[Pos] != '(')
return Proto.substr(Pos++, 1);
size_t Start = Pos + 1;
size_t End = Proto.find(')', Start);
assert_with_loc(End != StringRef::npos, "unmatched modifier group paren");
Pos = End + 1;
return Proto.slice(Start, End);
}
std::string Intrinsic::getInstTypeCode(Type T, ClassKind CK) const { std::string Intrinsic::getInstTypeCode(Type T, ClassKind CK) const {
char typeCode = '\0'; char typeCode = '\0';
bool printNumber = true; bool printNumber = true;
@ -955,13 +1053,17 @@ std::string Intrinsic::getInstTypeCode(Type T, ClassKind CK) const {
return S; return S;
} }
static bool isFloatingPointProtoModifier(char Mod) {
return Mod == 'F' || Mod == 'f' || Mod == 'H' || Mod == 'Y' || Mod == 'I';
}
std::string Intrinsic::getBuiltinTypeStr() { std::string Intrinsic::getBuiltinTypeStr() {
ClassKind LocalCK = getClassKind(true); ClassKind LocalCK = getClassKind(true);
std::string S; std::string S;
Type RetT = getReturnType(); Type RetT = getReturnType();
if ((LocalCK == ClassI || LocalCK == ClassW) && RetT.isScalar() && if ((LocalCK == ClassI || LocalCK == ClassW) && RetT.isScalar() &&
!RetT.isFloating()) !RetT.isFloating() && !RetT.isVoid())
RetT.makeInteger(RetT.getElementSizeInBits(), false); RetT.makeInteger(RetT.getElementSizeInBits(), false);
// Since the return value must be one type, return a vector type of the // Since the return value must be one type, return a vector type of the
@ -976,7 +1078,7 @@ std::string Intrinsic::getBuiltinTypeStr() {
if (!RetT.isScalar() && RetT.isInteger() && !RetT.isSigned()) if (!RetT.isScalar() && RetT.isInteger() && !RetT.isSigned())
RetT.makeSigned(); RetT.makeSigned();
if (LocalCK == ClassB && RetT.isValue() && !RetT.isScalar()) if (LocalCK == ClassB && !RetT.isVoid() && !RetT.isScalar())
// Cast to vector of 8-bit elements. // Cast to vector of 8-bit elements.
RetT.makeInteger(8, true); RetT.makeInteger(8, true);
@ -1092,7 +1194,7 @@ void Intrinsic::initVariables() {
// Modify the TypeSpec per-argument to get a concrete Type, and create // Modify the TypeSpec per-argument to get a concrete Type, and create
// known variables for each. // known variables for each.
for (unsigned I = 1; I < Types.size(); ++I) { for (unsigned I = 1; I < Proto.size(); ++I) {
char NameC = '0' + (I - 1); char NameC = '0' + (I - 1);
std::string Name = "p"; std::string Name = "p";
Name.push_back(NameC); Name.push_back(NameC);
@ -1213,7 +1315,7 @@ void Intrinsic::emitShadowedArgs() {
for (unsigned I = 0; I < getNumParams(); ++I) { for (unsigned I = 0; I < getNumParams(); ++I) {
// Do not create a temporary for an immediate argument. // Do not create a temporary for an immediate argument.
// That would defeat the whole point of using a macro! // That would defeat the whole point of using a macro!
if (getParamType(I).isImmediate()) if (hasImmediate() && Proto[I+1] == 'i')
continue; continue;
// Do not create a temporary for pointer arguments. The input // Do not create a temporary for pointer arguments. The input
// pointer may have an alignment hint. // pointer may have an alignment hint.
@ -1237,9 +1339,13 @@ void Intrinsic::emitShadowedArgs() {
} }
bool Intrinsic::protoHasScalar() const { bool Intrinsic::protoHasScalar() const {
return std::any_of(Types.begin(), Types.end(), [](const Type &T) { return (Proto.find('s') != std::string::npos ||
return T.isScalar() && !T.isImmediate(); Proto.find('z') != std::string::npos ||
}); Proto.find('r') != std::string::npos ||
Proto.find('b') != std::string::npos ||
Proto.find('$') != std::string::npos ||
Proto.find('y') != std::string::npos ||
Proto.find('o') != std::string::npos);
} }
void Intrinsic::emitBodyAsBuiltinCall() { void Intrinsic::emitBodyAsBuiltinCall() {
@ -1302,7 +1408,13 @@ void Intrinsic::emitBodyAsBuiltinCall() {
// Extra constant integer to hold type class enum for this function, e.g. s8 // Extra constant integer to hold type class enum for this function, e.g. s8
if (getClassKind(true) == ClassB) { if (getClassKind(true) == ClassB) {
S += utostr(getPolymorphicKeyType().getNeonEnum()); Type ThisTy = getReturnType();
if (Proto[0] == 'v' || isFloatingPointProtoModifier(Proto[0]))
ThisTy = getParamType(0);
if (ThisTy.isPointer())
ThisTy = getParamType(1);
S += utostr(ThisTy.getNeonEnum());
} else { } else {
// Remove extraneous ", ". // Remove extraneous ", ".
S.pop_back(); S.pop_back();
@ -1907,9 +2019,9 @@ void NeonEmitter::createIntrinsic(Record *R,
std::vector<std::pair<TypeSpec, TypeSpec>> NewTypeSpecs; std::vector<std::pair<TypeSpec, TypeSpec>> NewTypeSpecs;
for (auto TS : TypeSpecs) { for (auto TS : TypeSpecs) {
if (CartesianProductOfTypes) { if (CartesianProductOfTypes) {
Type DefaultT(TS, "."); Type DefaultT(TS, 'd');
for (auto SrcTS : TypeSpecs) { for (auto SrcTS : TypeSpecs) {
Type DefaultSrcT(SrcTS, "."); Type DefaultSrcT(SrcTS, 'd');
if (TS == SrcTS || if (TS == SrcTS ||
DefaultSrcT.getSizeInBits() != DefaultT.getSizeInBits()) DefaultSrcT.getSizeInBits() != DefaultT.getSizeInBits())
continue; continue;
@ -1989,19 +2101,31 @@ void NeonEmitter::genOverloadTypeCheckCode(raw_ostream &OS,
continue; continue;
uint64_t Mask = 0ULL; uint64_t Mask = 0ULL;
Mask |= 1ULL << Def->getPolymorphicKeyType().getNeonEnum(); Type Ty = Def->getReturnType();
if (Def->getProto()[0] == 'v' ||
isFloatingPointProtoModifier(Def->getProto()[0]))
Ty = Def->getParamType(0);
if (Ty.isPointer())
Ty = Def->getParamType(1);
Mask |= 1ULL << Ty.getNeonEnum();
// Check if the function has a pointer or const pointer argument. // Check if the function has a pointer or const pointer argument.
std::string Proto = Def->getProto();
int PtrArgNum = -1; int PtrArgNum = -1;
bool HasConstPtr = false; bool HasConstPtr = false;
for (unsigned I = 0; I < Def->getNumParams(); ++I) { for (unsigned I = 0; I < Def->getNumParams(); ++I) {
const auto &Type = Def->getParamType(I); char ArgType = Proto[I + 1];
if (Type.isPointer()) { if (ArgType == 'c') {
HasConstPtr = true;
PtrArgNum = I; PtrArgNum = I;
HasConstPtr = Type.isConstPointer(); break;
}
if (ArgType == 'p') {
PtrArgNum = I;
break;
} }
} }
// For sret builtins, adjust the pointer argument index. // For sret builtins, adjust the pointer argument index.
if (PtrArgNum >= 0 && Def->getReturnType().getNumVectors() > 1) if (PtrArgNum >= 0 && Def->getReturnType().getNumVectors() > 1)
PtrArgNum += 1; PtrArgNum += 1;
@ -2225,7 +2349,7 @@ void NeonEmitter::run(raw_ostream &OS) {
bool InIfdef = false; bool InIfdef = false;
for (auto &TS : TDTypeVec) { for (auto &TS : TDTypeVec) {
bool IsA64 = false; bool IsA64 = false;
Type T(TS, "."); Type T(TS, 'd');
if (T.isDouble() || (T.isPoly() && T.getElementSizeInBits() == 64)) if (T.isDouble() || (T.isPoly() && T.getElementSizeInBits() == 64))
IsA64 = true; IsA64 = true;
@ -2258,7 +2382,7 @@ void NeonEmitter::run(raw_ostream &OS) {
for (unsigned NumMembers = 2; NumMembers <= 4; ++NumMembers) { for (unsigned NumMembers = 2; NumMembers <= 4; ++NumMembers) {
for (auto &TS : TDTypeVec) { for (auto &TS : TDTypeVec) {
bool IsA64 = false; bool IsA64 = false;
Type T(TS, "."); Type T(TS, 'd');
if (T.isDouble() || (T.isPoly() && T.getElementSizeInBits() == 64)) if (T.isDouble() || (T.isPoly() && T.getElementSizeInBits() == 64))
IsA64 = true; IsA64 = true;
@ -2271,8 +2395,8 @@ void NeonEmitter::run(raw_ostream &OS) {
InIfdef = true; InIfdef = true;
} }
const char Mods[] = { static_cast<char>('2' + (NumMembers - 2)), 0}; char M = '2' + (NumMembers - 2);
Type VT(TS, Mods); Type VT(TS, M);
OS << "typedef struct " << VT.str() << " {\n"; OS << "typedef struct " << VT.str() << " {\n";
OS << " " << T.str() << " val"; OS << " " << T.str() << " val";
OS << "[" << NumMembers << "]"; OS << "[" << NumMembers << "]";

View File

@ -1,172 +0,0 @@
#!/usr/bin/env python3
# This script was committed on 20/11/2019 and it would probably make sense to remove
# it after the next release branches.
# This script is pipe based and converts an arm_neon.td (or arm_fp16.td) file
# using the old single-char type modifiers to an equivalent new-style form where
# each modifier is orthogonal and they can be composed.
#
# It was used to directly generate the .td files on master, so if you have any
# local additions I would suggest implementing any modifiers here, and running
# it over your entire pre-merge .td files rather than trying to resolve any
# conflicts manually.
import re, sys
MOD_MAP = {
'v': 'v',
'x': 'S',
'u': 'U',
'd': '.',
'g': 'q',
'j': 'Q',
'w': '>Q',
'n': '>',
'h': '<',
'q': '<Q',
'e': '<U',
'm': '<q',
'i': 'I',
'l': 'IU>',
's': '1',
'z': '1<',
'r': '1>',
'b': '1U',
'$': '1S',
'k': 'Q',
'2': '2',
'3': '3',
'4': '4',
'B': '2Q',
'C': '3Q',
'D': '4Q',
'p': '*',
'c': 'c*',
'7': '<<q',
'8': '<<',
'9': '<<Q',
't': 'p'
}
def typespec_elt_size(typespec):
if 'c' in typespec:
return 8
elif 's' in typespec or 'h' in typespec:
return 16
elif 'i' in typespec or 'f' in typespec:
return 32
elif 'l' in typespec or 'd' in typespec:
return 64
elif 'k' in typespec:
return 128
def get_resize(cur, desired):
res = ''
while cur < desired:
res += '>'
cur *= 2
while cur > desired:
res += '<'
cur /= 2
return res
def remap_protocol(proto, typespec, name):
key_type = 0
# Conversions like to see the integer type so they know signedness.
if 'vcvt' in name and '_f' in name and name != 'vcvt_f32_f64' and name != 'vcvt_f64_f32':
key_type = 1
default_width = typespec_elt_size(typespec)
inconsistent_width = False
for elt in typespec:
new_width = typespec_elt_size(elt)
if new_width and new_width != default_width:
inconsistent_width = True
res = ''
for i, c in enumerate(proto):
# void and pointers make for bad discriminators in CGBuiltin.cpp.
if c in 'vcp':
key_type += 1
if c in MOD_MAP:
cur_mod = MOD_MAP[c]
elif inconsistent_width:
# Otherwise it's a fixed output width modifier.
sys.stderr.write(f'warning: {name} uses fixed output size but has inconsistent input widths: {proto} {typespec}\n')
if c == 'Y':
# y: scalar of half float
resize = get_resize(default_width, 16)
cur_mod = f'1F{resize}'
elif c == 'y':
# y: scalar of float
resize = get_resize(default_width, 32)
cur_mod = f'1F{resize}'
elif c == 'o':
# o: scalar of double
resize = get_resize(default_width, 64)
cur_mod = f'1F{resize}'
elif c == 'I':
# I: scalar of 32-bit signed
resize = get_resize(default_width, 32)
cur_mod = f'1S{resize}'
elif c == 'L':
# L: scalar of 64-bit signed
resize = get_resize(default_width, 64)
cur_mod = f'1S{resize}'
elif c == 'U':
# I: scalar of 32-bit unsigned
resize = get_resize(default_width, 32)
cur_mod = f'1U{resize}'
elif c == 'O':
# O: scalar of 64-bit unsigned
resize = get_resize(default_width, 64)
cur_mod = f'1U{resize}'
elif c == 'f':
# f: float (int args)
resize = get_resize(default_width, 32)
cur_mod = f'F{resize}'
elif c == 'F':
# F: double (int args)
resize = get_resize(default_width, 64)
cur_mod = f'F{resize}'
elif c == 'H':
# H: half (int args)
resize = get_resize(default_width, 16)
cur_mod = f'F{resize}'
elif c == '0':
# 0: half (int args), ignore 'Q' size modifier.
resize = get_resize(default_width, 16)
cur_mod = f'Fq{resize}'
elif c == '1':
# 1: half (int args), force 'Q' size modifier.
resize = get_resize(default_width, 16)
cur_mod = f'FQ{resize}'
if len(cur_mod) == 0:
raise Exception(f'WTF: {c} in {name}')
if key_type != 0 and key_type == i:
cur_mod += '!'
if len(cur_mod) == 1:
res += cur_mod
else:
res += '(' + cur_mod + ')'
return res
def replace_insts(m):
start, end = m.span('proto')
start -= m.start()
end -= m.start()
new_proto = remap_protocol(m['proto'], m['kinds'], m['name'])
return m.group()[:start] + new_proto + m.group()[end:]
INST = re.compile(r'Inst<"(?P<name>.*?)",\s*"(?P<proto>.*?)",\s*"(?P<kinds>.*?)"')
new_td = INST.sub(replace_insts, sys.stdin.read())
sys.stdout.write(new_td)