Skip to content

Commit a6bfe12

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

File tree

5 files changed

+203
-1
lines changed

5 files changed

+203
-1
lines changed

ddmd/hooks.d

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ module ddmd.hooks;
1010
import ddmd.dscope;
1111
import ddmd.expression;
1212

13+
import gen.ldctraits;
14+
15+
/// Returns `null` when the __trait was not recognized.
1316
Expression semanticTraitsHook(TraitsExp e, Scope* sc)
1417
{
15-
return null;
18+
return semanticTraitsLDC(e, sc);
1619
}

ddmd/idgen.d

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

398402

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/semantic/target_traits.d

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

0 commit comments

Comments
 (0)