Skip to content

Commit 613fe94

Browse files
authored
Add llvm convert vector to ldc.intrinsics (#5160)
1 parent 28933e1 commit 613fe94

6 files changed

Lines changed: 201 additions & 2 deletions

File tree

gen/pragma.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ LDCPragma DtoGetPragma(Scope *sc, PragmaDeclaration *decl,
134134
{"bitop.bt", LLVMbitop_bt}, {"bitop.btc", LLVMbitop_btc},
135135
{"bitop.btr", LLVMbitop_btr}, {"bitop.bts", LLVMbitop_bts},
136136
{"bitop.vld", LLVMbitop_vld}, {"bitop.vst", LLVMbitop_vst},
137+
{"convertvector", LLVMconvertvector},
137138
};
138139

139140
static std::string prefix = "ldc.";
@@ -414,7 +415,8 @@ void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *s,
414415
break;
415416
}
416417

417-
case LLVMatomic_rmw: {
418+
case LLVMatomic_rmw:
419+
case LLVMconvertvector: {
418420
const int count = applyTemplatePragma(s, [=](TemplateDeclaration *td) {
419421
td->llvmInternal = llvm_internal;
420422
td->intrinsicName = arg1str;
@@ -595,6 +597,7 @@ bool DtoIsMagicIntrinsic(FuncDeclaration *fd) {
595597
case LLVMbitop_bts:
596598
case LLVMbitop_vld:
597599
case LLVMbitop_vst:
600+
case LLVMconvertvector:
598601
return true;
599602

600603
default:

gen/pragma.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ enum LDCPragma {
4747
LLVMbitop_bts,
4848
LLVMbitop_vld,
4949
LLVMbitop_vst,
50+
LLVMconvertvector,
5051
LLVMextern_weak,
5152
LLVMprofile_instr
5253
};

gen/tocall.cpp

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,68 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e,
627627
return true;
628628
}
629629

630+
if (fndecl->llvmInternal == LLVMconvertvector) {
631+
if (e->arguments->length != 1) {
632+
error(e->loc, "`convertvector` intrinsic expects 1 argument");
633+
fatal();
634+
}
635+
636+
Expression *exp1 = (*e->arguments)[0];
637+
LLValue *srcVal = DtoRVal(exp1);
638+
LLType *toLLType = DtoType(e->type);
639+
640+
auto *fromVecTy = llvm::cast<llvm::FixedVectorType>(srcVal->getType());
641+
LLType *fromElemTy = fromVecTy->getElementType();
642+
643+
auto *toVecTy = llvm::cast<llvm::FixedVectorType>(toLLType);
644+
LLType *toElemTy = toVecTy->getElementType();
645+
646+
Type *fromDType = exp1->type->toBasetype();
647+
Type *toDType = e->type->toBasetype();
648+
assert(fromDType->ty == TY::Tvector && toDType->ty == TY::Tvector);
649+
Type *fromElemDType = static_cast<TypeVector *>(fromDType)->elementType();
650+
Type *toElemDType = static_cast<TypeVector *>(toDType)->elementType();
651+
bool fromUnsigned = fromElemDType->isUnsigned();
652+
bool toUnsigned = toElemDType->isUnsigned();
653+
654+
unsigned fromBits = fromElemTy->getPrimitiveSizeInBits();
655+
unsigned toBits = toElemTy->getPrimitiveSizeInBits();
656+
657+
LLValue *val;
658+
if (fromElemTy->isIntegerTy() && toElemTy->isIntegerTy()) {
659+
if (fromBits < toBits) {
660+
val = fromUnsigned
661+
? p->ir->CreateZExt(srcVal, toLLType)
662+
: p->ir->CreateSExt(srcVal, toLLType);
663+
} else if (fromBits > toBits) {
664+
val = p->ir->CreateTrunc(srcVal, toLLType);
665+
} else {
666+
val = srcVal;
667+
}
668+
} else if (fromElemTy->isIntegerTy() && toElemTy->isFloatingPointTy()) {
669+
val = fromUnsigned ? p->ir->CreateUIToFP(srcVal, toLLType)
670+
: p->ir->CreateSIToFP(srcVal, toLLType);
671+
} else if (fromElemTy->isFloatingPointTy() && toElemTy->isIntegerTy()) {
672+
val = toUnsigned ? p->ir->CreateFPToUI(srcVal, toLLType)
673+
: p->ir->CreateFPToSI(srcVal, toLLType);
674+
} else if (fromElemTy->isFloatingPointTy() && toElemTy->isFloatingPointTy()) {
675+
if (fromBits < toBits) {
676+
val = p->ir->CreateFPExt(srcVal, toLLType);
677+
} else if (fromBits > toBits) {
678+
val = p->ir->CreateFPTrunc(srcVal, toLLType);
679+
} else {
680+
val = srcVal;
681+
}
682+
} else {
683+
error(e->loc, "unsupported vector element conversion from `%s` to `%s`",
684+
exp1->type->toChars(), e->type->toChars());
685+
fatal();
686+
}
687+
688+
result = new DImValue(e->type, val);
689+
return true;
690+
}
691+
630692
return false;
631693
}
632694

@@ -734,7 +796,7 @@ class ImplicitArgumentsBuilder {
734796
}
735797
args.push_back(thisptrLval);
736798
} else if (thiscall && dfnval && dfnval->vthis) {
737-
799+
738800
if (objccall && directcall) {
739801

740802
// ... or a Objective-c direct call argument

runtime/druntime/src/ldc/intrinsics.di

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,22 @@ pragma(LDC_intrinsic, "llvm.assume")
696696
pragma(LDC_intrinsic, "llvm.sideeffect")
697697
void llvm_sideeffect();
698698

699+
/// Performs element-wise type conversion between two vector types with the
700+
/// same number of elements. The source and destination vectors must have the
701+
/// same element count but may differ in element type and element size.
702+
/// The conversion follows standard D conversion rules:
703+
/// - Integer-to-integer: sext (signed) or zext (unsigned) for widening, trunc for narrowing
704+
/// - Integer-to-float: sitofp (signed) or uitofp (unsigned)
705+
/// - Float-to-integer: fptosi (signed dest) or fptoui (unsigned dest)
706+
/// - Float-to-float: fpext for widening, fptrunc for narrowing
707+
///
708+
/// This is the equivalent of GDC/Clang's `__builtin_convertvector`.
709+
pragma(LDC_intrinsic, "ldc.convertvector")
710+
To llvm_convertvector(To, From)(From val)
711+
if (is(From : __vector(V[N]), V, size_t N) &&
712+
is(To : __vector(U[M]), U, size_t M) &&
713+
N == M);
714+
699715
version (WebAssembly)
700716
{
701717
/// Grows memory by a given delta and returns the previous size, or -1 if enough
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// RUN: %ldc -output-ll -of=%t.ll %s && FileCheck %s < %t.ll
2+
3+
import core.simd;
4+
import ldc.intrinsics;
5+
6+
// CHECK-LABEL: define {{.*}}@{{.*}}test_fptrunc
7+
// CHECK: fptrunc <2 x double> {{.*}} to <2 x float>
8+
float2 test_fptrunc(double2 v) {
9+
return llvm_convertvector!(float2)(v);
10+
}
11+
12+
// CHECK-LABEL: define {{.*}}@{{.*}}test_fpext
13+
// CHECK: fpext <2 x float> {{.*}} to <2 x double>
14+
double2 test_fpext(float2 v) {
15+
return llvm_convertvector!(double2)(v);
16+
}
17+
18+
// CHECK-LABEL: define {{.*}}@{{.*}}test_sext
19+
// CHECK: sext <4 x i16> {{.*}} to <4 x i32>
20+
int4 test_sext(short4 v) {
21+
return llvm_convertvector!(int4)(v);
22+
}
23+
24+
// CHECK-LABEL: define {{.*}}@{{.*}}test_zext
25+
// CHECK: zext <4 x i16> {{.*}} to <4 x i32>
26+
uint4 test_zext(ushort4 v) {
27+
return llvm_convertvector!(uint4)(v);
28+
}
29+
30+
// CHECK-LABEL: define {{.*}}@{{.*}}test_trunc
31+
// CHECK: trunc <4 x i64> {{.*}} to <4 x i32>
32+
int4 test_trunc(long4 v) {
33+
return llvm_convertvector!(int4)(v);
34+
}
35+
36+
// CHECK-LABEL: define {{.*}}@{{.*}}test_sitofp
37+
// CHECK: sitofp <4 x i32> {{.*}} to <4 x float>
38+
float4 test_sitofp(int4 v) {
39+
return llvm_convertvector!(float4)(v);
40+
}
41+
42+
// CHECK-LABEL: define {{.*}}@{{.*}}test_uitofp
43+
// CHECK: uitofp <4 x i32> {{.*}} to <4 x float>
44+
float4 test_uitofp(uint4 v) {
45+
return llvm_convertvector!(float4)(v);
46+
}
47+
48+
// CHECK-LABEL: define {{.*}}@{{.*}}test_fptosi
49+
// CHECK: fptosi <4 x float> {{.*}} to <4 x i32>
50+
int4 test_fptosi(float4 v) {
51+
return llvm_convertvector!(int4)(v);
52+
}
53+
54+
// CHECK-LABEL: define {{.*}}@{{.*}}test_fptoui
55+
// CHECK: fptoui <4 x float> {{.*}} to <4 x i32>
56+
uint4 test_fptoui(float4 v) {
57+
return llvm_convertvector!(uint4)(v);
58+
}
59+
60+
// CHECK-LABEL: define {{.*}}@{{.*}}test_same_width_int
61+
// Same-width int-to-int should be a no-op (no conversion instruction, same type)
62+
// CHECK-NOT: {{(sext|zext|trunc|sitofp|uitofp|fptosi|fptoui|fptrunc|fpext)}}
63+
int4 test_same_width_int(int4 v) {
64+
return llvm_convertvector!(int4)(v);
65+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: %ldc -run %s
2+
3+
import core.simd;
4+
import ldc.intrinsics;
5+
6+
void main()
7+
{
8+
// Float-to-float: narrowing (double -> float)
9+
const double2 d2 = [ 1.5, -2.5 ];
10+
const f2_narrow = llvm_convertvector!(float2)(d2);
11+
assert(f2_narrow is [ 1.5f, -2.5f ]);
12+
13+
// Float-to-float: widening (float -> double)
14+
const float2 f2 = [ 3.25f, -1.75f ];
15+
const d2_widen = llvm_convertvector!(double2)(f2);
16+
assert(d2_widen is [ 3.25, -1.75 ]);
17+
18+
// Int-to-int: widening signed (short -> int)
19+
const short4 s4 = [ 1, -2, 3, -4 ];
20+
const i4_widen = llvm_convertvector!(int4)(s4);
21+
assert(i4_widen is [ 1, -2, 3, -4 ]);
22+
23+
// Int-to-int: widening unsigned (ushort -> uint)
24+
const ushort4 us4 = [ 1, 2, 3, 4 ];
25+
const ui4_widen = llvm_convertvector!(uint4)(us4);
26+
assert(ui4_widen is [ 1, 2, 3, 4 ]);
27+
28+
// Int-to-int: narrowing (long -> int)
29+
const long4 l4 = [ 1000000L, -2000000L, 3000000L, -4000000L ];
30+
const i4_narrow = llvm_convertvector!(int4)(l4);
31+
assert(i4_narrow is [ 1000000, -2000000, 3000000, -4000000 ]);
32+
33+
// Int-to-float: signed
34+
const int4 i4 = [ 1, -2, 3, -4 ];
35+
const f4_from_i4 = llvm_convertvector!(float4)(i4);
36+
assert(f4_from_i4 is [ 1.0f, -2.0f, 3.0f, -4.0f ]);
37+
38+
// Int-to-float: unsigned
39+
const uint4 ui4 = [ 1, 2, 3, 4 ];
40+
const f4_from_ui4 = llvm_convertvector!(float4)(ui4);
41+
assert(f4_from_ui4 is [ 1.0f, 2.0f, 3.0f, 4.0f ]);
42+
43+
// Float-to-int: signed dest
44+
const float4 f4 = [ 1.5f, -2.7f, 3.2f, -4.9f ];
45+
const i4_from_f4 = llvm_convertvector!(int4)(f4);
46+
assert(i4_from_f4 is [ 1, -2, 3, -4 ]);
47+
48+
// Float-to-int: unsigned dest
49+
const float4 f4p = [ 1.5f, 2.7f, 3.2f, 4.9f ];
50+
const ui4_from_f4 = llvm_convertvector!(uint4)(f4p);
51+
assert(ui4_from_f4 is [ 1, 2, 3, 4 ]);
52+
}

0 commit comments

Comments
 (0)