Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 1 addition & 72 deletions src/CodeGen_ARM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,77 +35,6 @@ using namespace llvm;

namespace {

// Populate feature flags in a target according to those implied by
// existing flags, so that instruction patterns can just check for the
// oldest feature flag that supports an instruction.
//
// According to LLVM, ARM architectures have the following is-a-superset-of
// relationships:
//
// v9.5a > v9.4a > v9.3a > v9.2a > v9.1a > v9a;
// v v v v v
// v8.9a > v8.8a > v8.7a > v8.6a > v8.5a > v8.4a > ... > v8a;
//
// v8r has no relation to anything.
Target complete_arm_target(Target t) {
if (t.os == Target::OSX) {
// The Apple M1 implements the full ARM v8.4a spec.
t.set_feature(Target::ARMv84a);
}

auto add_implied_feature_if_supported = [](Target &t, Target::Feature super, Target::Feature implied) {
if (t.has_feature(super)) {
t.set_feature(implied);
}
};

// ARMFp16 implies ARMv8.2-A; we don't know of any devices where
// that doesn't hold. The cascade loop below will set ARMv81a and ARMv8a.
add_implied_feature_if_supported(t, Target::ARMFp16, Target::ARMv82a);

constexpr int num_arm_v8_features = 10;
static const Target::Feature arm_v8_features[num_arm_v8_features] = {
// The following loop depends on this array being sorted correctly.
// keep-sorted start numeric=yes order=desc
Target::ARMv89a,
Target::ARMv88a,
Target::ARMv87a,
Target::ARMv86a,
Target::ARMv85a,
Target::ARMv84a,
Target::ARMv83a,
Target::ARMv82a,
Target::ARMv81a,
Target::ARMv8a,
// keep-sorted end
};

for (int i = 0; i < num_arm_v8_features - 1; i++) {
add_implied_feature_if_supported(t,
arm_v8_features[i],
arm_v8_features[i + 1]);
}

static const Target::Feature features_with_fp16[] = {
Target::SVE,
Target::SVE2,
};

for (const auto &f : features_with_fp16) {
add_implied_feature_if_supported(t, f, Target::ARMFp16);
}

static const Target::Feature features_with_dotprod[] = {
Target::SVE2,
};

for (const auto &f : features_with_dotprod) {
add_implied_feature_if_supported(t, f, Target::ARMDotProd);
}

return t;
}

// Substitute in loads that feed into slicing shuffles, to help with vld2/3/4
// emission. These are commonly lifted as lets because they get used by multiple
// interleaved slices of the same load.
Expand Down Expand Up @@ -301,7 +230,7 @@ class CodeGen_ARM : public CodeGen_CPU {
};

CodeGen_ARM::CodeGen_ARM(const Target &target)
: CodeGen_CPU(complete_arm_target(target)) {
: CodeGen_CPU(target) {

// TODO(https://github.com/halide/Halide/issues/8088): See if
// use_llvm_vp_intrinsics can replace architecture specific code in this
Expand Down
5 changes: 5 additions & 0 deletions src/CodeGen_LLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ void CodeGen_LLVM::set_context(llvm::LLVMContext &context) {
}

std::unique_ptr<CodeGen_LLVM> CodeGen_LLVM::new_for_target(const Target &target, llvm::LLVMContext &context) {
// Code generation inspects the target's features to decide which
// instructions are available, so it expects a target with all implied
// features already set (e.g. AVX2 implies AVX, SSE41, ...). This is
// guaranteed for the module produced by lower(), and for the host target
// used to compile JIT trampolines.
std::unique_ptr<CodeGen_LLVM> result;
if (target.arch == Target::X86) {
result = new_CodeGen_X86(target);
Expand Down
69 changes: 7 additions & 62 deletions src/CodeGen_X86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,53 +27,6 @@ using namespace llvm;

namespace {

// Populate feature flags in a target according to those implied by
// existing flags, so that instruction patterns can just check for the
// oldest feature flag that supports an instruction.
Target complete_x86_target(Target t) {
if (t.has_feature(Target::AVX10_1)) {
if (t.vector_bits >= 256) {
t.set_feature(Target::AVX2);
}
if (t.vector_bits >= 512) {
t.set_feature(Target::AVX512_SapphireRapids);
}
}
if (t.has_feature(Target::AVX512_SapphireRapids)) {
t.set_feature(Target::AVX512_Zen4);
t.set_feature(Target::AVXVNNI);
}
if (t.has_feature(Target::AVX512_Zen5)) {
t.set_feature(Target::AVX512_Zen4);
t.set_feature(Target::AVXVNNI);
}
if (t.has_feature(Target::AVX512_Zen4)) {
t.set_feature(Target::AVX512_Cannonlake);
}
if (t.has_feature(Target::AVX512_Cannonlake)) {
t.set_feature(Target::AVX512_Skylake);
}
if (t.has_feature(Target::AVX512_Cannonlake) ||
t.has_feature(Target::AVX512_Skylake) ||
t.has_feature(Target::AVX512_KNL)) {
t.set_feature(Target::AVX512);
}
if (t.has_feature(Target::AVX512)) {
t.set_feature(Target::AVX2);
}
if (t.has_feature(Target::AVX2)) {
t.set_feature(Target::AVX);
// All AVX2-enabled architectures have F16C and FMA
t.set_feature(Target::F16C);
t.set_feature(Target::FMA);
}
if (t.has_feature(Target::AVX)) {
t.set_feature(Target::SSE41);
}

return t;
}

/** A code generator that emits x86 code from a given Halide stmt. */
class CodeGen_X86 : public CodeGen_CPU {
public:
Expand Down Expand Up @@ -121,7 +74,7 @@ class CodeGen_X86 : public CodeGen_CPU {
};

CodeGen_X86::CodeGen_X86(Target t)
: CodeGen_CPU(complete_x86_target(t)) {
: CodeGen_CPU(t) {
}

const int max_intrinsic_args = 6;
Expand Down Expand Up @@ -1734,7 +1687,7 @@ string CodeGen_X86::mcpu_target() const {
} else if (target.has_feature(Target::AVX2)) {
// x86-64-v3: SSE4.2, POPCNT, AVX, AVX2, BMI1/2, F16C, FMA,
// LZCNT, MOVBE. Also covers AVX512 / AVX512_KNL, since both
// imply AVX2 (via complete_x86_target), but neither requires
// imply AVX2 (via set_implied_features), but neither requires
// BW/DQ/VL which would come for free with v4.
return "x86-64-v3";
} else if (target.has_feature(Target::AVX)) {
Expand Down Expand Up @@ -1857,24 +1810,20 @@ string CodeGen_X86::mattrs() const {
}

// AVX512 features. Any AVX512 variant implies AVX2 (via
// complete_x86_target), so the mcpu baseline is at least
// set_implied_features), so the mcpu baseline is at least
// x86-64-v3. Skylake-and-above selects x86-64-v4, which already
// includes F/CD/BW/DQ/VL, but we still add those features
// explicitly so the bare AVX512 / AVX512_KNL paths (which use
// x86-64-v3) also get them.
if (target.has_feature(Target::AVX512) ||
target.has_feature(Target::AVX512_KNL) ||
target.has_feature(Target::AVX512_Skylake) ||
target.has_feature(Target::AVX512_Cannonlake)) {
if (target.has_feature(Target::AVX512)) {
attrs.emplace_back("+avx512f");
attrs.emplace_back("+avx512cd");
}
if (target.has_feature(Target::AVX512_KNL)) {
attrs.emplace_back("+avx512pf");
attrs.emplace_back("+avx512er");
}
if (target.has_feature(Target::AVX512_Skylake) ||
target.has_feature(Target::AVX512_Cannonlake)) {
if (target.has_feature(Target::AVX512_Skylake)) {
attrs.emplace_back("+avx512vl");
attrs.emplace_back("+avx512bw");
attrs.emplace_back("+avx512dq");
Expand Down Expand Up @@ -1931,13 +1880,9 @@ bool CodeGen_X86::use_soft_float_abi() const {
int CodeGen_X86::native_vector_bits() const {
if (target.has_feature(Target::AVX10_1)) {
return target.vector_bits;
} else if (target.has_feature(Target::AVX512) ||
target.has_feature(Target::AVX512_Skylake) ||
target.has_feature(Target::AVX512_KNL) ||
target.has_feature(Target::AVX512_Cannonlake)) {
} else if (target.has_feature(Target::AVX512)) {
return 512;
} else if (target.has_feature(Target::AVX) ||
target.has_feature(Target::AVX2)) {
} else if (target.has_feature(Target::AVX)) {
return 256;
} else {
return 128;
Expand Down
3 changes: 2 additions & 1 deletion src/IRPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ ostream &operator<<(ostream &stream, const Module &m) {
stream << s << "\n";
}

stream << "module name=" << m.name() << ", target=" << m.target().to_string() << "\n";
// The module retains implied features, but print it in minimal form.
stream << "module name=" << m.name() << ", target=" << m.target().without_implied_features().to_string() << "\n";
for (const auto &b : m.buffers()) {
stream << b << "\n";
}
Expand Down
9 changes: 7 additions & 2 deletions src/Lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -615,9 +615,14 @@ Module lower(const vector<Function> &output_funcs,
const vector<Stmt> &requirements,
bool trace_pipeline,
const vector<IRMutator *> &custom_passes) {
Module result_module{strip_namespaces(pipeline_name), t};
// Lowering and code generation inspect a target with all implied features
// set, so that (e.g.) a check for SSE41 succeeds on an AVX2 target.
// Normalize once here; the module retains the implied features, and is
// printed back in minimal form by unsetting them at the print sites.
Target target = t.with_implied_features();
Module result_module{strip_namespaces(pipeline_name), target};
run_with_large_stack([&]() {
lower_impl(output_funcs, pipeline_name, t, args, linkage_type, requirements, trace_pipeline, custom_passes, result_module);
lower_impl(output_funcs, pipeline_name, target, args, linkage_type, requirements, trace_pipeline, custom_passes, result_module);
});
return result_module;
}
Expand Down
5 changes: 4 additions & 1 deletion src/Pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,10 @@ Module Pipeline::compile_to_module(const vector<Argument> &args,

const Module &old_module = contents->module;

bool same_compile = !old_module.functions().empty() && old_module.target() == target;
// A lowered module stores the target with implied features set, so compare
// against the same normalized form of the requested target.
bool same_compile = !old_module.functions().empty() &&
old_module.target() == target.with_implied_features();
// Either generated name or one of the LoweredFuncs in the existing module has the same name.
same_compile = same_compile && fn_name.empty();
bool found_name = false;
Expand Down
6 changes: 4 additions & 2 deletions src/StmtToHTML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,8 @@ class HTMLCodePrinter : public IRVisitor {
// -- print text
print_opening_tag("span", "matched");
print_html_element("span", "keyword", "module");
print_text(" name=" + m.name() + ", target=" + m.target().to_string());
// The module retains implied features, but print it in minimal form.
print_text(" name=" + m.name() + ", target=" + m.target().without_implied_features().to_string());
print_closing_tag("span");

// Open code block to hold module body
Expand Down Expand Up @@ -767,7 +768,8 @@ class HTMLCodePrinter : public IRVisitor {
// -- print text
print_opening_tag("span", "matched");
print_html_element("span", "keyword", "module");
print_text(" name=" + m.name() + ", target=" + m.target().to_string());
// The module retains implied features, but print it in minimal form.
print_text(" name=" + m.name() + ", target=" + m.target().without_implied_features().to_string());
print_closing_tag("span");

// Open code block to hold module body
Expand Down
Loading
Loading