mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-01 17:51:48 -07:00
@(require_target_feature=<string>) @(enable_target_feature=<string>)
require_target_feature - required by the target micro-architecture enable_target_feature - will be enabled for the specified procedure only
This commit is contained in:
+107
-2
@@ -256,7 +256,6 @@ struct BuildContext {
|
||||
String extra_linker_flags;
|
||||
String extra_assembler_flags;
|
||||
String microarch;
|
||||
String target_features;
|
||||
BuildModeKind build_mode;
|
||||
bool generate_docs;
|
||||
i32 optimization_level;
|
||||
@@ -320,6 +319,10 @@ struct BuildContext {
|
||||
|
||||
PtrMap<char const *, ExactValue> defined_values;
|
||||
|
||||
BlockingMutex target_features_mutex;
|
||||
StringSet target_features_set;
|
||||
String target_features_string;
|
||||
|
||||
};
|
||||
|
||||
gb_global BuildContext build_context = {0};
|
||||
@@ -1197,6 +1200,100 @@ void init_build_context(TargetMetrics *cross_target) {
|
||||
#include "microsoft_craziness.h"
|
||||
#endif
|
||||
|
||||
|
||||
Array<String> split_by_comma(String const &list) {
|
||||
isize n = 1;
|
||||
for (isize i = 0; i < list.len; i++) {
|
||||
if (list.text[i] == ',') {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
auto res = array_make<String>(heap_allocator(), n);
|
||||
|
||||
String s = list;
|
||||
for (isize i = 0; i < n; i++) {
|
||||
isize m = string_index_byte(s, ',');
|
||||
if (m < 0) {
|
||||
res[i] = s;
|
||||
break;
|
||||
}
|
||||
res[i] = substring(s, 0, m);
|
||||
s = substring(s, m+1, s.len);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool check_target_feature_is_valid(TokenPos pos, String const &feature) {
|
||||
// TODO(bill): check_target_feature_is_valid
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_target_feature_is_enabled(TokenPos pos, String const &target_feature_list) {
|
||||
BuildContext *bc = &build_context;
|
||||
mutex_lock(&bc->target_features_mutex);
|
||||
defer (mutex_unlock(&bc->target_features_mutex));
|
||||
|
||||
auto items = split_by_comma(target_feature_list);
|
||||
array_free(&items);
|
||||
for_array(i, items) {
|
||||
String const &item = items.data[i];
|
||||
if (!check_target_feature_is_valid(pos, item)) {
|
||||
error(pos, "Target feature '%.*s' is not valid", LIT(item));
|
||||
return false;
|
||||
}
|
||||
if (!string_set_exists(&bc->target_features_set, item)) {
|
||||
error(pos, "Target feature '%.*s' is not enabled", LIT(item));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void enable_target_feature(TokenPos pos, String const &target_feature_list) {
|
||||
BuildContext *bc = &build_context;
|
||||
mutex_lock(&bc->target_features_mutex);
|
||||
defer (mutex_unlock(&bc->target_features_mutex));
|
||||
|
||||
auto items = split_by_comma(target_feature_list);
|
||||
array_free(&items);
|
||||
for_array(i, items) {
|
||||
String const &item = items.data[i];
|
||||
if (!check_target_feature_is_valid(pos, item)) {
|
||||
error(pos, "Target feature '%.*s' is not valid", LIT(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quotes) {
|
||||
isize len = 0;
|
||||
for_array(i, build_context.target_features_set.entries) {
|
||||
if (i != 0) {
|
||||
len += 1;
|
||||
}
|
||||
String feature = build_context.target_features_set.entries[i].value;
|
||||
len += feature.len;
|
||||
if (with_quotes) len += 2;
|
||||
}
|
||||
char *features = gb_alloc_array(allocator, char, len+1);
|
||||
len = 0;
|
||||
for_array(i, build_context.target_features_set.entries) {
|
||||
if (i != 0) {
|
||||
features[len++] = ',';
|
||||
}
|
||||
|
||||
if (with_quotes) features[len++] = '"';
|
||||
String feature = build_context.target_features_set.entries[i].value;
|
||||
gb_memmove(features, feature.text, feature.len);
|
||||
len += feature.len;
|
||||
if (with_quotes) features[len++] = '"';
|
||||
}
|
||||
features[len++] = 0;
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate.
|
||||
// We've previously called `parse_build_flags`, so `out_filepath` should be set.
|
||||
bool init_build_paths(String init_filename) {
|
||||
@@ -1206,6 +1303,9 @@ bool init_build_paths(String init_filename) {
|
||||
// NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index.
|
||||
array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT);
|
||||
|
||||
string_set_init(&bc->target_features_set, heap_allocator(), 1024);
|
||||
mutex_init(&bc->target_features_mutex);
|
||||
|
||||
// [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path.
|
||||
bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename);
|
||||
|
||||
@@ -1382,5 +1482,10 @@ bool init_build_paths(String init_filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bc->target_features_string.len != 0) {
|
||||
enable_target_feature({}, bc->target_features_string);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -899,6 +899,18 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
}
|
||||
}
|
||||
|
||||
if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
|
||||
error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together");
|
||||
} else if (ac.require_target_feature.len != 0) {
|
||||
if (check_target_feature_is_enabled(e->token.pos, ac.require_target_feature)) {
|
||||
e->Procedure.target_feature = ac.require_target_feature;
|
||||
} else {
|
||||
e->Procedure.target_feature_disabled = true;
|
||||
}
|
||||
} else if (ac.enable_target_feature.len != 0) {
|
||||
enable_target_feature(e->token.pos, ac.enable_target_feature);
|
||||
e->Procedure.target_feature = ac.enable_target_feature;
|
||||
}
|
||||
|
||||
switch (e->Procedure.optimization_mode) {
|
||||
case ProcedureOptimizationMode_None:
|
||||
|
||||
@@ -3207,6 +3207,22 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (name == "require_target_feature") {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ev.kind == ExactValue_String) {
|
||||
ac->require_target_feature = ev.value_string;
|
||||
} else {
|
||||
error(elem, "Expected a string value for '%.*s'", LIT(name));
|
||||
}
|
||||
return true;
|
||||
} else if (name == "enable_target_feature") {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ev.kind == ExactValue_String) {
|
||||
ac->enable_target_feature = ev.value_string;
|
||||
} else {
|
||||
error(elem, "Expected a string value for '%.*s'", LIT(name));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -124,6 +124,9 @@ struct AttributeContext {
|
||||
String objc_name;
|
||||
bool objc_is_class_method;
|
||||
Type * objc_type;
|
||||
|
||||
String require_target_feature; // required by the target micro-architecture
|
||||
String enable_target_feature; // will be enabled for the procedure only
|
||||
};
|
||||
|
||||
AttributeContext make_attribute_context(String link_prefix) {
|
||||
|
||||
+5
-3
@@ -233,10 +233,12 @@ struct Entity {
|
||||
String link_name;
|
||||
String link_prefix;
|
||||
DeferredProcedure deferred_procedure;
|
||||
bool is_foreign;
|
||||
bool is_export;
|
||||
bool generated_from_polymorphic;
|
||||
ProcedureOptimizationMode optimization_mode;
|
||||
bool is_foreign : 1;
|
||||
bool is_export : 1;
|
||||
bool generated_from_polymorphic : 1;
|
||||
bool target_feature_disabled : 1;
|
||||
String target_feature;
|
||||
} Procedure;
|
||||
struct {
|
||||
Array<Entity *> entities;
|
||||
|
||||
@@ -1332,8 +1332,8 @@ void lb_generate_code(lbGenerator *gen) {
|
||||
}
|
||||
}
|
||||
|
||||
if (build_context.target_features.len != 0) {
|
||||
llvm_features = alloc_cstring(permanent_allocator(), build_context.target_features);
|
||||
if (build_context.target_features_set.entries.count != 0) {
|
||||
llvm_features = target_features_set_to_cstring(permanent_allocator(), false);
|
||||
}
|
||||
|
||||
// GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target));
|
||||
|
||||
@@ -169,6 +169,19 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
|
||||
}
|
||||
}
|
||||
|
||||
if (!entity->Procedure.target_feature_disabled &&
|
||||
entity->Procedure.target_feature.len != 0) {
|
||||
auto features = split_by_comma(entity->Procedure.target_feature);
|
||||
for_array(i, features) {
|
||||
String feature = features[i];
|
||||
LLVMAttributeRef ref = LLVMCreateStringAttribute(
|
||||
m->ctx,
|
||||
cast(char const *)feature.text, cast(unsigned)feature.len,
|
||||
"", 0);
|
||||
LLVMAddAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, ref);
|
||||
}
|
||||
}
|
||||
|
||||
if (entity->flags & EntityFlag_Cold) {
|
||||
lb_add_attribute_to_proc(m, p->value, "cold");
|
||||
}
|
||||
|
||||
+2
-2
@@ -1376,8 +1376,8 @@ bool parse_build_flags(Array<String> args) {
|
||||
}
|
||||
case BuildFlag_TargetFeatures: {
|
||||
GB_ASSERT(value.kind == ExactValue_String);
|
||||
build_context.target_features = value.value_string;
|
||||
string_to_lower(&build_context.target_features);
|
||||
build_context.target_features_string = value.value_string;
|
||||
string_to_lower(&build_context.target_features_string);
|
||||
break;
|
||||
}
|
||||
case BuildFlag_RelocMode: {
|
||||
|
||||
@@ -157,6 +157,15 @@ int string_compare(String const &x, String const &y) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
isize string_index_byte(String const &s, u8 x) {
|
||||
for (isize i = 0; i < s.len; i++) {
|
||||
if (s.text[i] == x) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
GB_COMPARE_PROC(string_cmp_proc) {
|
||||
String x = *(String *)a;
|
||||
String y = *(String *)b;
|
||||
|
||||
Reference in New Issue
Block a user