[ARM,MVE] Add intrinsics for vector get/set lane.

This adds the `vgetq_lane` and `vsetq_lane` families, to copy between
a scalar and a specified lane of a vector.

One of the new `vgetq_lane` intrinsics returns a `float16_t`, which
causes a compile error if `%clang_cc1` doesn't get the option
`-fallow-half-arguments-and-returns`. The driver passes that option to
cc1 already, but I've had to edit all the explicit cc1 command lines
in the existing MVE intrinsics tests.

A couple of fixes are included for the code I wrote up front in
MveEmitter to support lane-index immediates (and which nothing has
tested until now): the type was wrong (`uint32_t` instead of `int`)
and the range was off by one.

I've also added a method of bypassing the default promotion to `i32`
that is done by the MveEmitter code generation: it's sensible to
promote short scalars like `i16` to `i32` if they're going to be
passed to custom IR intrinsics representing a machine instruction
operating on GPRs, but not if they're going to be passed to standard
IR operations like `insertelement` which expect the exact type.

Reviewers: ostannard, MarkMurrayARM, dmgreen

Reviewed By: dmgreen

Subscribers: kristof.beyls, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D70188
This commit is contained in:
Simon Tatham 2019-11-15 09:53:15 +00:00
parent 902e84556a
commit 9e37892773
14 changed files with 381 additions and 23 deletions

View File

@ -413,4 +413,9 @@ let params = T.All in {
def vuninitializedq_polymorphic: Intrinsic<
Vector, (args Vector), (undef Vector)>;
}
def vgetq_lane: Intrinsic<Scalar, (args Vector:$v, imm_lane:$lane),
(xelt_var $v, $lane)>;
def vsetq_lane: Intrinsic<Vector, (args unpromoted<Scalar>:$e, Vector:$v, imm_lane:$lane),
(ielt_var $v, $e, $lane)>;
}

View File

@ -77,6 +77,8 @@ def xval: IRBuilder<"CreateExtractValue"> {
def ielt_const: IRBuilder<"CreateInsertElement"> {
let special_params = [IRBuilderIntParam<2, "uint64_t">];
}
def ielt_var: IRBuilder<"CreateInsertElement">;
def xelt_var: IRBuilder<"CreateExtractElement">;
def trunc: IRBuilder<"CreateTrunc">;
def bitcast: IRBuilder<"CreateBitCast">;
def extend: CGHelperFn<"SignOrZeroExtend"> {
@ -172,6 +174,10 @@ def CTO_CopyKind: ComplexTypeOp;
// of _s32 / _f16 / _u8 suffix.
def Void : Type;
// A wrapper you can put on an intrinsic's argument type to prevent it from
// being automatically promoted to i32 from a smaller integer type.
class unpromoted<Type t> : Type { Type underlying_type = t; }
// Primitive types: base class, and an instance for the set of scalar integer
// and floating types that MVE uses.
class PrimitiveType<string kind_, int size_>: Type {
@ -285,7 +291,7 @@ def imm_0toNm1 : Immediate<u32, IB_EltBit<0>>;
// imm_lane has to be the index of a vector lane in the main vector type, i.e
// it can range from 0 to (128 / size of scalar)-1 inclusive. (e.g. vgetq_lane)
def imm_lane : Immediate<u32, IB_LaneIndex>;
def imm_lane : Immediate<sint, IB_LaneIndex>;
// imm_1to32 can be in the range 1 to 32, unconditionally. (e.g. scalar shift
// intrinsics)

View File

@ -0,0 +1,291 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg -sroa -early-cse | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -DPOLYMORPHIC -S -emit-llvm -o - %s | opt -S -mem2reg -sroa -early-cse | FileCheck %s
#include <arm_mve.h>
// CHECK-LABEL: @test_vgetq_lane_f16(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = extractelement <8 x half> [[A:%.*]], i32 2
// CHECK-NEXT: [[TMP1:%.*]] = bitcast half [[TMP0]] to i16
// CHECK-NEXT: [[TMP_0_INSERT_EXT:%.*]] = zext i16 [[TMP1]] to i32
// CHECK-NEXT: [[TMP2:%.*]] = bitcast i32 [[TMP_0_INSERT_EXT]] to float
// CHECK-NEXT: ret float [[TMP2]]
//
float16_t test_vgetq_lane_f16(float16x8_t a)
{
#ifdef POLYMORPHIC
return vgetq_lane(a, 2);
#else /* POLYMORPHIC */
return vgetq_lane_f16(a, 2);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vgetq_lane_f32(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = extractelement <4 x float> [[A:%.*]], i32 3
// CHECK-NEXT: ret float [[TMP0]]
//
float32_t test_vgetq_lane_f32(float32x4_t a)
{
#ifdef POLYMORPHIC
return vgetq_lane(a, 3);
#else /* POLYMORPHIC */
return vgetq_lane_f32(a, 3);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vgetq_lane_s16(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = extractelement <8 x i16> [[A:%.*]], i32 4
// CHECK-NEXT: ret i16 [[TMP0]]
//
int16_t test_vgetq_lane_s16(int16x8_t a)
{
#ifdef POLYMORPHIC
return vgetq_lane(a, 4);
#else /* POLYMORPHIC */
return vgetq_lane_s16(a, 4);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vgetq_lane_s32(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = extractelement <4 x i32> [[A:%.*]], i32 0
// CHECK-NEXT: ret i32 [[TMP0]]
//
int32_t test_vgetq_lane_s32(int32x4_t a)
{
#ifdef POLYMORPHIC
return vgetq_lane(a, 0);
#else /* POLYMORPHIC */
return vgetq_lane_s32(a, 0);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vgetq_lane_s64(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = extractelement <2 x i64> [[A:%.*]], i32 0
// CHECK-NEXT: ret i64 [[TMP0]]
//
int64_t test_vgetq_lane_s64(int64x2_t a)
{
#ifdef POLYMORPHIC
return vgetq_lane(a, 0);
#else /* POLYMORPHIC */
return vgetq_lane_s64(a, 0);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vgetq_lane_s8(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = extractelement <16 x i8> [[A:%.*]], i32 10
// CHECK-NEXT: ret i8 [[TMP0]]
//
int8_t test_vgetq_lane_s8(int8x16_t a)
{
#ifdef POLYMORPHIC
return vgetq_lane(a, 10);
#else /* POLYMORPHIC */
return vgetq_lane_s8(a, 10);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vgetq_lane_u16(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = extractelement <8 x i16> [[A:%.*]], i32 3
// CHECK-NEXT: ret i16 [[TMP0]]
//
uint16_t test_vgetq_lane_u16(uint16x8_t a)
{
#ifdef POLYMORPHIC
return vgetq_lane(a, 3);
#else /* POLYMORPHIC */
return vgetq_lane_u16(a, 3);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vgetq_lane_u32(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = extractelement <4 x i32> [[A:%.*]], i32 3
// CHECK-NEXT: ret i32 [[TMP0]]
//
uint32_t test_vgetq_lane_u32(uint32x4_t a)
{
#ifdef POLYMORPHIC
return vgetq_lane(a, 3);
#else /* POLYMORPHIC */
return vgetq_lane_u32(a, 3);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vgetq_lane_u64(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = extractelement <2 x i64> [[A:%.*]], i32 1
// CHECK-NEXT: ret i64 [[TMP0]]
//
uint64_t test_vgetq_lane_u64(uint64x2_t a)
{
#ifdef POLYMORPHIC
return vgetq_lane(a, 1);
#else /* POLYMORPHIC */
return vgetq_lane_u64(a, 1);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vgetq_lane_u8(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = extractelement <16 x i8> [[A:%.*]], i32 1
// CHECK-NEXT: ret i8 [[TMP0]]
//
uint8_t test_vgetq_lane_u8(uint8x16_t a)
{
#ifdef POLYMORPHIC
return vgetq_lane(a, 1);
#else /* POLYMORPHIC */
return vgetq_lane_u8(a, 1);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vsetq_lane_f16(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = bitcast float [[A_COERCE:%.*]] to i32
// CHECK-NEXT: [[TMP_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[TMP0]] to i16
// CHECK-NEXT: [[TMP1:%.*]] = bitcast i16 [[TMP_0_EXTRACT_TRUNC]] to half
// CHECK-NEXT: [[TMP2:%.*]] = insertelement <8 x half> [[B:%.*]], half [[TMP1]], i32 4
// CHECK-NEXT: ret <8 x half> [[TMP2]]
//
float16x8_t test_vsetq_lane_f16(float16_t a, float16x8_t b)
{
#ifdef POLYMORPHIC
return vsetq_lane(a, b, 4);
#else /* POLYMORPHIC */
return vsetq_lane_f16(a, b, 4);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vsetq_lane_f32(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = insertelement <4 x float> [[B:%.*]], float [[A:%.*]], i32 2
// CHECK-NEXT: ret <4 x float> [[TMP0]]
//
float32x4_t test_vsetq_lane_f32(float32_t a, float32x4_t b)
{
#ifdef POLYMORPHIC
return vsetq_lane(a, b, 2);
#else /* POLYMORPHIC */
return vsetq_lane_f32(a, b, 2);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vsetq_lane_s8(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = insertelement <16 x i8> [[B:%.*]], i8 [[A:%.*]], i32 12
// CHECK-NEXT: ret <16 x i8> [[TMP0]]
//
int8x16_t test_vsetq_lane_s8(int8_t a, int8x16_t b)
{
#ifdef POLYMORPHIC
return vsetq_lane(a, b, 12);
#else /* POLYMORPHIC */
return vsetq_lane_s8(a, b, 12);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vsetq_lane_s16(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = insertelement <8 x i16> [[B:%.*]], i16 [[A:%.*]], i32 6
// CHECK-NEXT: ret <8 x i16> [[TMP0]]
//
int16x8_t test_vsetq_lane_s16(int16_t a, int16x8_t b)
{
#ifdef POLYMORPHIC
return vsetq_lane(a, b, 6);
#else /* POLYMORPHIC */
return vsetq_lane_s16(a, b, 6);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vsetq_lane_s32(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = insertelement <4 x i32> [[B:%.*]], i32 [[A:%.*]], i32 2
// CHECK-NEXT: ret <4 x i32> [[TMP0]]
//
int32x4_t test_vsetq_lane_s32(int32_t a, int32x4_t b)
{
#ifdef POLYMORPHIC
return vsetq_lane(a, b, 2);
#else /* POLYMORPHIC */
return vsetq_lane_s32(a, b, 2);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vsetq_lane_s64(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = insertelement <2 x i64> [[B:%.*]], i64 [[A:%.*]], i32 0
// CHECK-NEXT: ret <2 x i64> [[TMP0]]
//
int64x2_t test_vsetq_lane_s64(int64_t a, int64x2_t b)
{
#ifdef POLYMORPHIC
return vsetq_lane(a, b, 0);
#else /* POLYMORPHIC */
return vsetq_lane_s64(a, b, 0);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vsetq_lane_u8(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = insertelement <16 x i8> [[B:%.*]], i8 [[A:%.*]], i32 2
// CHECK-NEXT: ret <16 x i8> [[TMP0]]
//
uint8x16_t test_vsetq_lane_u8(uint8_t a, uint8x16_t b)
{
#ifdef POLYMORPHIC
return vsetq_lane(a, b, 2);
#else /* POLYMORPHIC */
return vsetq_lane_u8(a, b, 2);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vsetq_lane_u16(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = insertelement <8 x i16> [[B:%.*]], i16 [[A:%.*]], i32 7
// CHECK-NEXT: ret <8 x i16> [[TMP0]]
//
uint16x8_t test_vsetq_lane_u16(uint16_t a, uint16x8_t b)
{
#ifdef POLYMORPHIC
return vsetq_lane(a, b, 7);
#else /* POLYMORPHIC */
return vsetq_lane_u16(a, b, 7);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vsetq_lane_u32(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = insertelement <4 x i32> [[B:%.*]], i32 [[A:%.*]], i32 0
// CHECK-NEXT: ret <4 x i32> [[TMP0]]
//
uint32x4_t test_vsetq_lane_u32(uint32_t a, uint32x4_t b)
{
#ifdef POLYMORPHIC
return vsetq_lane(a, b, 0);
#else /* POLYMORPHIC */
return vsetq_lane_u32(a, b, 0);
#endif /* POLYMORPHIC */
}
// CHECK-LABEL: @test_vsetq_lane_u64(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = insertelement <2 x i64> [[B:%.*]], i64 [[A:%.*]], i32 1
// CHECK-NEXT: ret <2 x i64> [[TMP0]]
//
uint64x2_t test_vsetq_lane_u64(uint64_t a, uint64x2_t b)
{
#ifdef POLYMORPHIC
return vsetq_lane(a, b, 1);
#else /* POLYMORPHIC */
return vsetq_lane_u64(a, b, 1);
#endif /* POLYMORPHIC */
}

View File

@ -1,6 +1,6 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -DPOLYMORPHIC -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -DPOLYMORPHIC -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
#include <arm_mve.h>

View File

@ -1,5 +1,5 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
#include <arm_mve.h>

View File

@ -1,6 +1,6 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -DPOLYMORPHIC -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -DPOLYMORPHIC -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
#include <arm_mve.h>

View File

@ -1,6 +1,6 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -DPOLYMORPHIC -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -DPOLYMORPHIC -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
#include <arm_mve.h>

View File

@ -1,6 +1,6 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -DPOLYMORPHIC -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -DPOLYMORPHIC -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
#include <arm_mve.h>

View File

@ -1,5 +1,5 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
#include <arm_mve.h>

View File

@ -1,6 +1,6 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg -sroa -early-cse | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -DPOLYMORPHIC -S -emit-llvm -o - %s | opt -S -mem2reg -sroa -early-cse | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg -sroa -early-cse | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -DPOLYMORPHIC -S -emit-llvm -o - %s | opt -S -mem2reg -sroa -early-cse | FileCheck %s
#include <arm_mve.h>

View File

@ -1,5 +1,5 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
#include <arm_mve.h>

View File

@ -1,6 +1,6 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -O0 -disable-O0-optnone -DPOLYMORPHIC -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -mfloat-abi hard -fallow-half-arguments-and-returns -O0 -disable-O0-optnone -DPOLYMORPHIC -S -emit-llvm -o - %s | opt -S -mem2reg | FileCheck %s
#include <arm_mve.h>

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -verify -fsyntax-only %s
// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -fallow-half-arguments-and-returns -target-feature +mve.fp -verify -fsyntax-only %s
#include <arm_mve.h>
@ -54,3 +54,47 @@ void test_load_offsets(uint32x4_t addr32, uint64x2_t addr64)
vstrwq_scatter_base(addr32, 2, addr32); // expected-error {{argument should be a multiple of 4}}
vstrwq_scatter_base(addr32, 1, addr32); // expected-error {{argument should be a multiple of 4}}
}
void test_lane_indices(uint8x16_t v16, uint16x8_t v8,
uint32x4_t v4, uint64x2_t v2)
{
vgetq_lane_u8(v16, -1); // expected-error {{argument value -1 is outside the valid range [0, 15]}}
vgetq_lane_u8(v16, 0);
vgetq_lane_u8(v16, 15);
vgetq_lane_u8(v16, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
vgetq_lane_u16(v8, -1); // expected-error {{argument value -1 is outside the valid range [0, 7]}}
vgetq_lane_u16(v8, 0);
vgetq_lane_u16(v8, 7);
vgetq_lane_u16(v8, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}
vgetq_lane_u32(v4, -1); // expected-error {{argument value -1 is outside the valid range [0, 3]}}
vgetq_lane_u32(v4, 0);
vgetq_lane_u32(v4, 3);
vgetq_lane_u32(v4, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
vgetq_lane_u64(v2, -1); // expected-error {{argument value -1 is outside the valid range [0, 1]}}
vgetq_lane_u64(v2, 0);
vgetq_lane_u64(v2, 1);
vgetq_lane_u64(v2, 2); // expected-error {{argument value 2 is outside the valid range [0, 1]}}
vsetq_lane_u8(23, v16, -1); // expected-error {{argument value -1 is outside the valid range [0, 15]}}
vsetq_lane_u8(23, v16, 0);
vsetq_lane_u8(23, v16, 15);
vsetq_lane_u8(23, v16, 16); // expected-error {{argument value 16 is outside the valid range [0, 15]}}
vsetq_lane_u16(23, v8, -1); // expected-error {{argument value -1 is outside the valid range [0, 7]}}
vsetq_lane_u16(23, v8, 0);
vsetq_lane_u16(23, v8, 7);
vsetq_lane_u16(23, v8, 8); // expected-error {{argument value 8 is outside the valid range [0, 7]}}
vsetq_lane_u32(23, v4, -1); // expected-error {{argument value -1 is outside the valid range [0, 3]}}
vsetq_lane_u32(23, v4, 0);
vsetq_lane_u32(23, v4, 3);
vsetq_lane_u32(23, v4, 4); // expected-error {{argument value 4 is outside the valid range [0, 3]}}
vsetq_lane_u64(23, v2, -1); // expected-error {{argument value -1 is outside the valid range [0, 1]}}
vsetq_lane_u64(23, v2, 0);
vsetq_lane_u64(23, v2, 1);
vsetq_lane_u64(23, v2, 2); // expected-error {{argument value 2 is outside the valid range [0, 1]}}
}

View File

@ -980,7 +980,8 @@ public:
const Type *Param);
Result::Ptr getCodeForDagArg(DagInit *D, unsigned ArgNum,
const Result::Scope &Scope, const Type *Param);
Result::Ptr getCodeForArg(unsigned ArgNum, const Type *ArgType);
Result::Ptr getCodeForArg(unsigned ArgNum, const Type *ArgType,
bool Promote);
// Constructor and top-level functions.
@ -1003,8 +1004,13 @@ const Type *MveEmitter::getType(Init *I, const Type *Param) {
}
const Type *MveEmitter::getType(Record *R, const Type *Param) {
// Pass to a subfield of any wrapper records. We don't expect more than one
// of these: immediate operands are used as plain numbers rather than as
// llvm::Value, so it's meaningless to promote their type anyway.
if (R->isSubClassOf("Immediate"))
R = R->getValueAsDef("type"); // pass to subfield
R = R->getValueAsDef("type");
else if (R->isSubClassOf("unpromoted"))
R = R->getValueAsDef("underlying_type");
if (R->getName() == "Void")
return getVoidType();
@ -1197,12 +1203,13 @@ Result::Ptr MveEmitter::getCodeForDagArg(DagInit *D, unsigned ArgNum,
PrintFatalError("bad dag argument type for code generation");
}
Result::Ptr MveEmitter::getCodeForArg(unsigned ArgNum, const Type *ArgType) {
Result::Ptr MveEmitter::getCodeForArg(unsigned ArgNum, const Type *ArgType,
bool Promote) {
Result::Ptr V =
std::make_shared<BuiltinArgResult>(ArgNum, isa<PointerType>(ArgType));
if (const auto *ST = dyn_cast<ScalarType>(ArgType)) {
if (ST->isInteger() && ST->sizeInBits() < 32)
if (Promote && ST->isInteger() && ST->sizeInBits() < 32)
V = std::make_shared<IntCastResult>(getScalarType("u32"), V);
} else if (const auto *PT = dyn_cast<PredicateType>(ArgType)) {
V = std::make_shared<IntCastResult>(getScalarType("u32"), V);
@ -1260,6 +1267,11 @@ ACLEIntrinsic::ACLEIntrinsic(MveEmitter &ME, Record *R, const Type *Param)
for (unsigned i = 0, e = ArgsDag->getNumArgs(); i < e; ++i) {
Init *TypeInit = ArgsDag->getArg(i);
bool Promote = true;
if (auto TypeDI = dyn_cast<DefInit>(TypeInit))
if (TypeDI->getDef()->isSubClassOf("unpromoted"))
Promote = false;
// Work out the type of the argument, for use in the function prototype in
// the header file.
const Type *ArgType = ME.getType(TypeInit, Param);
@ -1269,7 +1281,7 @@ ACLEIntrinsic::ACLEIntrinsic(MveEmitter &ME, Record *R, const Type *Param)
// into the variable-name scope that the code gen will refer to.
StringRef ArgName = ArgsDag->getArgNameStr(i);
if (!ArgName.empty())
Scope[ArgName] = ME.getCodeForArg(i, ArgType);
Scope[ArgName] = ME.getCodeForArg(i, ArgType, Promote);
// If the argument is a subclass of Immediate, record the details about
// what values it can take, for Sema checking.
@ -1288,7 +1300,7 @@ ACLEIntrinsic::ACLEIntrinsic(MveEmitter &ME, Record *R, const Type *Param)
} else if (Bounds->getName() == "IB_LaneIndex") {
IA.boundsType = ImmediateArg::BoundsType::ExplicitRange;
IA.i1 = 0;
IA.i2 = 128 / Param->sizeInBits();
IA.i2 = 128 / Param->sizeInBits() - 1;
} else if (Bounds->getName() == "IB_EltBit") {
IA.boundsType = ImmediateArg::BoundsType::ExplicitRange;
IA.i1 = Bounds->getValueAsInt("base");