Skip to content

Commit 34e2896

Browse files
committed
Add LDC-specific traits for CTFE information about the target machine.
__traits(targetCPU) == "broadwell" __traits(targetHasFeature, "sse2") == bool
1 parent 3b1f042 commit 34e2896

File tree

5 files changed

+210
-0
lines changed

5 files changed

+210
-0
lines changed

ddmd/idgen.d

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,10 @@ Msgtable[] msgtable =
392392
{ "LDC_global_crt_ctor" },
393393
{ "LDC_global_crt_dtor" },
394394
{ "LDC_extern_weak" },
395+
396+
// IN_LLVM: LDC-specific traits.
397+
{ "targetCPU" },
398+
{ "targetHasFeature" },
395399
];
396400

397401

ddmd/traits.d

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ import ddmd.root.stringtable;
3939
import ddmd.tokens;
4040
import ddmd.visitor;
4141

42+
version(IN_LLVM)
43+
{
44+
import gen.ldctraits;
45+
}
46+
4247
enum LOGSEMANTIC = false;
4348

4449
/************************ TraitsExp ************************************/
@@ -1188,6 +1193,14 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc)
11881193
{
11891194
return pointerBitmap(e);
11901195
}
1196+
version(IN_LLVM)
1197+
{
1198+
// Check compiler vendor specific traits
1199+
if (Expression ret = semanticTraitsLDC(e, sc))
1200+
{
1201+
return ret;
1202+
}
1203+
}
11911204

11921205
extern (D) void* trait_search_fp(const(char)* seed, ref int cost)
11931206
{

gen/ldctraits.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//===-- ldctraits.cpp -----------------------------------------------------===//
2+
//
3+
// LDC – the LLVM D compiler
4+
//
5+
// This file is distributed under the BSD-style LDC license. See the LICENSE
6+
// file for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "gen/irstate.h"
11+
#include "llvm/Target/TargetMachine.h"
12+
#include "llvm/MC/MCSubtargetInfo.h"
13+
14+
// TODO: move this to a D interfacing helper file
15+
struct Dstring {
16+
const char *ptr;
17+
size_t len;
18+
};
19+
20+
Dstring traitsGetTargetCPU() {
21+
auto cpu = gTargetMachine->getTargetCPU();
22+
return {cpu.data(), cpu.size()};
23+
}
24+
25+
bool traitsTargetHasFeature(Dstring feature) {
26+
#if LDC_LLVM_VER < 307
27+
// LLVM below 3.7 does not provide the necessary means to obtain the needed information,
28+
// return the safe "feature not enabled".
29+
return false;
30+
#else
31+
auto feat = llvm::StringRef(feature.ptr, feature.len);
32+
33+
// This is a work-around to a missing interface in LLVM to query whether a
34+
// feature is set.
35+
36+
// Copy MCSubtargetInfo so we can modify it.
37+
llvm::MCSubtargetInfo mcinfo = *gTargetMachine->getMCSubtargetInfo();
38+
auto savedFeatbits = mcinfo.getFeatureBits();
39+
40+
// Nothing will change if the feature string is not recognized or if the
41+
// feature is disabled.
42+
{
43+
auto newFeatbits = mcinfo.ApplyFeatureFlag(("-" + feat).str());
44+
if (savedFeatbits == newFeatbits) {
45+
return false;
46+
}
47+
mcinfo.setFeatureBits(savedFeatbits);
48+
}
49+
{
50+
// Now that unrecognized feature strings are excluded,
51+
// nothing will change iff the feature and its implied features are enabled.
52+
auto newFeatbits = mcinfo.ApplyFeatureFlag(("+" + feat).str());
53+
return savedFeatbits == newFeatbits;
54+
}
55+
#endif
56+
}

gen/ldctraits.d

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//===-- gen/ldctraits.d - LDC-specific __traits handling ----------*- D -*-===//
2+
//
3+
// LDC – the LLVM D compiler
4+
//
5+
// This file is distributed under the BSD-style LDC license. See the LICENSE
6+
// file for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
//
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
module gen.ldctraits;
15+
16+
import ddmd.arraytypes;
17+
import ddmd.dscope;
18+
import ddmd.dtemplate;
19+
import ddmd.expression;
20+
import ddmd.errors;
21+
import ddmd.mtype;
22+
import ddmd.id;
23+
24+
// TODO: move this to a D interfacing helper file
25+
extern(C++) struct Dstring
26+
{
27+
const(char)* ptr;
28+
size_t len;
29+
};
30+
31+
extern(C++) Dstring traitsGetTargetCPU();
32+
extern(C++) bool traitsTargetHasFeature(Dstring feature);
33+
34+
Expression semanticTraitsLDC(TraitsExp e, Scope* sc)
35+
{
36+
size_t arg_count = e.args ? e.args.dim : 0;
37+
38+
if (e.ident == Id.targetCPU)
39+
{
40+
if (arg_count != 0)
41+
{
42+
e.warning("ignoring arguments for __traits %s", e.ident.toChars());
43+
}
44+
45+
auto cpu = traitsGetTargetCPU();
46+
auto se = new StringExp(e.loc, cast(void*)cpu.ptr, cpu.len);
47+
return se.semantic(sc);
48+
}
49+
if (e.ident == Id.targetHasFeature)
50+
{
51+
if (arg_count != 1)
52+
{
53+
e.error("__traits %s expects one argument, not %u", e.ident.toChars(), cast(uint)arg_count);
54+
return new ErrorExp();
55+
}
56+
57+
auto ex = isExpression((*e.args)[0]);
58+
if (!ex)
59+
{
60+
e.error("expression expected as argument of __traits %s", e.ident.toChars());
61+
return new ErrorExp();
62+
}
63+
ex = ex.ctfeInterpret();
64+
65+
StringExp se = ex.toStringExp();
66+
if (!se || se.len == 0)
67+
{
68+
e.error("string expected as argument of __traits %s instead of %s", e.ident.toChars(), ex.toChars());
69+
return new ErrorExp();
70+
}
71+
se = se.toUTF8(sc);
72+
if (se.sz != 1)
73+
{
74+
e.error("argument of __traits %s must be char string", e.ident.toChars());
75+
return new ErrorExp();
76+
}
77+
78+
auto featureFound = traitsTargetHasFeature(Dstring(se.toPtr(), se.len));
79+
return new IntegerExp(e.loc, featureFound ? 1 : 0, Type.tbool);
80+
}
81+
return null;
82+
}

tests/codegen/target_traits.d

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Tests LDC-specific target __traits
2+
3+
// RUN: %ldc -mcpu=haswell -d-version=CPU_HASWELL -c %s
4+
// RUN: %ldc -mcpu=pentium -mattr=+fma -d-version=ATTR_FMA -c %s
5+
// RUN: %ldc -mcpu=pentium -mattr=+fma,-sse -d-version=ATTR_FMA_MINUS_SSE -c %s
6+
7+
// Important: LLVM's default CPU selection already enables some features (like sse3)
8+
9+
// Querying feature information is only available from LLVM 3.7.
10+
// Below 3.7, __traits(targetHasFeature,...) should always return false.
11+
version (LDC_LLVM_305)
12+
{
13+
}
14+
else version (LDC_LLVM_306)
15+
{
16+
}
17+
else
18+
{
19+
version = HASFEATURE;
20+
}
21+
22+
void main()
23+
{
24+
version (CPU_HASWELL)
25+
{
26+
static assert(__traits(targetCPU) == "haswell");
27+
version (HASFEATURE)
28+
{
29+
static assert(__traits(targetHasFeature, "sse3"));
30+
static assert(__traits(targetHasFeature, "sse4.1"));
31+
}
32+
static assert(!__traits(targetHasFeature, "sse4"));
33+
static assert(!__traits(targetHasFeature, "sse4a"));
34+
static assert(!__traits(targetHasFeature, "unrecognized feature"));
35+
}
36+
version (ATTR_FMA)
37+
{
38+
version (HASFEATURE)
39+
{
40+
static assert(__traits(targetHasFeature, "sse"));
41+
static assert(__traits(targetHasFeature, "sse2"));
42+
static assert(__traits(targetHasFeature, "sse3"));
43+
static assert(__traits(targetHasFeature, "sse4.1"));
44+
static assert(__traits(targetHasFeature, "fma"));
45+
static assert(__traits(targetHasFeature, "avx"));
46+
}
47+
static assert(!__traits(targetHasFeature, "avx2"));
48+
static assert(!__traits(targetHasFeature, "unrecognized feature"));
49+
}
50+
version (ATTR_FMA_MINUS_SSE)
51+
{
52+
// All implied features must be enabled for targetHasFeature to return true
53+
static assert(!__traits(targetHasFeature, "fma"));
54+
}
55+
}

0 commit comments

Comments
 (0)