authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-05-04 18:19:49-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-05-04 18:19:49-07:00
logc95e49785772a54916418630fbc7283791a4aa72
tree4097d9cfef1b2ce5209410310480fad730f5a5ec
parentf2bc5ccbc2f322b625719d374bbb09a4335c1bd1

add cmpxchg builtin function


8 files changed, 213 insertions(+), 0 deletions(-)

src/all_types.hpp+10
...@@ -1115,6 +1115,7 @@ enum BuiltinFnId {...@@ -1115,6 +1115,7 @@ enum BuiltinFnId {
1115 BuiltinFnIdErrName,1115 BuiltinFnIdErrName,
1116 BuiltinFnIdBreakpoint,1116 BuiltinFnIdBreakpoint,
1117 BuiltinFnIdEmbedFile,1117 BuiltinFnIdEmbedFile,
1118 BuiltinFnIdCmpExchange,
1118};1119};
11191120
1120struct BuiltinFnEntry {1121struct BuiltinFnEntry {
...@@ -1183,6 +1184,7 @@ struct CodeGen {...@@ -1183,6 +1184,7 @@ struct CodeGen {
1183 TypeTableEntry *entry_os_enum;1184 TypeTableEntry *entry_os_enum;
1184 TypeTableEntry *entry_arch_enum;1185 TypeTableEntry *entry_arch_enum;
1185 TypeTableEntry *entry_environ_enum;1186 TypeTableEntry *entry_environ_enum;
1187 TypeTableEntry *entry_mem_order_enum;
1186 } builtin_types;1188 } builtin_types;
11871189
1188 ZigTarget zig_target;1190 ZigTarget zig_target;
...@@ -1322,6 +1324,14 @@ struct BlockContext {...@@ -1322,6 +1324,14 @@ struct BlockContext {
1322 bool safety_off;1324 bool safety_off;
1323};1325};
13241326
1327enum AtomicOrder {
1328 AtomicOrderUnordered,
1329 AtomicOrderMonotonic,
1330 AtomicOrderAcquire,
1331 AtomicOrderRelease,
1332 AtomicOrderAcqRel,
1333 AtomicOrderSeqCst,
1334};
13251335
13261336
1327#endif1337#endif
src/analyze.cpp+73
...@@ -4414,6 +4414,77 @@ static TypeTableEntry *analyze_embed_file(CodeGen *g, ImportTableEntry *import,...@@ -4414,6 +4414,77 @@ static TypeTableEntry *analyze_embed_file(CodeGen *g, ImportTableEntry *import,
4414 return resolve_expr_const_val_as_string_lit(g, node, &file_contents);4414 return resolve_expr_const_val_as_string_lit(g, node, &file_contents);
4415}4415}
44164416
4417static TypeTableEntry *analyze_cmpxchg(CodeGen *g, ImportTableEntry *import,
4418 BlockContext *context, AstNode *node)
4419{
4420 AstNode **ptr_arg = &node->data.fn_call_expr.params.at(0);
4421 AstNode **cmp_arg = &node->data.fn_call_expr.params.at(1);
4422 AstNode **new_arg = &node->data.fn_call_expr.params.at(2);
4423 AstNode **success_order_arg = &node->data.fn_call_expr.params.at(3);
4424 AstNode **failure_order_arg = &node->data.fn_call_expr.params.at(4);
4425
4426 TypeTableEntry *ptr_type = analyze_expression(g, import, context, nullptr, *ptr_arg);
4427 if (ptr_type->id == TypeTableEntryIdInvalid) {
4428 return g->builtin_types.entry_invalid;
4429 } else if (ptr_type->id != TypeTableEntryIdPointer) {
4430 add_node_error(g, *ptr_arg,
4431 buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&ptr_type->name)));
4432 return g->builtin_types.entry_invalid;
4433 }
4434
4435 TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
4436 TypeTableEntry *cmp_type = analyze_expression(g, import, context, child_type, *cmp_arg);
4437 TypeTableEntry *new_type = analyze_expression(g, import, context, child_type, *new_arg);
4438
4439 TypeTableEntry *success_order_type = analyze_expression(g, import, context,
4440 g->builtin_types.entry_mem_order_enum, *success_order_arg);
4441 TypeTableEntry *failure_order_type = analyze_expression(g, import, context,
4442 g->builtin_types.entry_mem_order_enum, *failure_order_arg);
4443
4444 if (cmp_type->id == TypeTableEntryIdInvalid ||
4445 new_type->id == TypeTableEntryIdInvalid ||
4446 success_order_type->id == TypeTableEntryIdInvalid ||
4447 failure_order_type->id == TypeTableEntryIdInvalid)
4448 {
4449 return g->builtin_types.entry_invalid;
4450 }
4451
4452 ConstExprValue *success_order_val = &get_resolved_expr(*success_order_arg)->const_val;
4453 ConstExprValue *failure_order_val = &get_resolved_expr(*failure_order_arg)->const_val;
4454 if (!success_order_val->ok) {
4455 add_node_error(g, *success_order_arg, buf_sprintf("unable to evaluate constant expression"));
4456 return g->builtin_types.entry_invalid;
4457 } else if (!failure_order_val->ok) {
4458 add_node_error(g, *failure_order_arg, buf_sprintf("unable to evaluate constant expression"));
4459 return g->builtin_types.entry_invalid;
4460 }
4461
4462 if (success_order_val->data.x_enum.tag < AtomicOrderMonotonic) {
4463 add_node_error(g, *success_order_arg,
4464 buf_sprintf("success atomic ordering must be Monotonic or stricter"));
4465 return g->builtin_types.entry_invalid;
4466 }
4467 if (failure_order_val->data.x_enum.tag < AtomicOrderMonotonic) {
4468 add_node_error(g, *failure_order_arg,
4469 buf_sprintf("failure atomic ordering must be Monotonic or stricter"));
4470 return g->builtin_types.entry_invalid;
4471 }
4472 if (failure_order_val->data.x_enum.tag > success_order_val->data.x_enum.tag) {
4473 add_node_error(g, *failure_order_arg,
4474 buf_sprintf("failure atomic ordering must be no stricter than success"));
4475 return g->builtin_types.entry_invalid;
4476 }
4477 if (failure_order_val->data.x_enum.tag == AtomicOrderRelease ||
4478 failure_order_val->data.x_enum.tag == AtomicOrderAcqRel)
4479 {
4480 add_node_error(g, *failure_order_arg,
4481 buf_sprintf("failure atomic ordering must not be Release or AcqRel"));
4482 return g->builtin_types.entry_invalid;
4483 }
4484
4485 return g->builtin_types.entry_bool;
4486}
4487
4417static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,4488static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
4418 TypeTableEntry *expected_type, AstNode *node)4489 TypeTableEntry *expected_type, AstNode *node)
4419{4490{
...@@ -4750,6 +4821,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry...@@ -4750,6 +4821,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
4750 return g->builtin_types.entry_void;4821 return g->builtin_types.entry_void;
4751 case BuiltinFnIdEmbedFile:4822 case BuiltinFnIdEmbedFile:
4752 return analyze_embed_file(g, import, context, node);4823 return analyze_embed_file(g, import, context, node);
4824 case BuiltinFnIdCmpExchange:
4825 return analyze_cmpxchg(g, import, context, node);
4753 }4826 }
4754 zig_unreachable();4827 zig_unreachable();
4755}4828}
src/codegen.cpp+80
...@@ -401,6 +401,46 @@ static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) {...@@ -401,6 +401,46 @@ static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) {
401 return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, "");401 return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, "");
402}402}
403403
404static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
405 switch (atomic_order) {
406 case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered;
407 case AtomicOrderMonotonic: return LLVMAtomicOrderingMonotonic;
408 case AtomicOrderAcquire: return LLVMAtomicOrderingAcquire;
409 case AtomicOrderRelease: return LLVMAtomicOrderingRelease;
410 case AtomicOrderAcqRel: return LLVMAtomicOrderingAcquireRelease;
411 case AtomicOrderSeqCst: return LLVMAtomicOrderingSequentiallyConsistent;
412 }
413 zig_unreachable();
414}
415
416static LLVMValueRef gen_cmp_exchange(CodeGen *g, AstNode *node) {
417 assert(node->type == NodeTypeFnCallExpr);
418
419 AstNode *ptr_arg = node->data.fn_call_expr.params.at(0);
420 AstNode *cmp_arg = node->data.fn_call_expr.params.at(1);
421 AstNode *new_arg = node->data.fn_call_expr.params.at(2);
422 AstNode *success_order_arg = node->data.fn_call_expr.params.at(3);
423 AstNode *failure_order_arg = node->data.fn_call_expr.params.at(4);
424
425 LLVMValueRef ptr_val = gen_expr(g, ptr_arg);
426 LLVMValueRef cmp_val = gen_expr(g, cmp_arg);
427 LLVMValueRef new_val = gen_expr(g, new_arg);
428
429 ConstExprValue *success_order_val = &get_resolved_expr(success_order_arg)->const_val;
430 ConstExprValue *failure_order_val = &get_resolved_expr(failure_order_arg)->const_val;
431
432 assert(success_order_val->ok);
433 assert(failure_order_val->ok);
434
435 LLVMAtomicOrdering success_order = to_LLVMAtomicOrdering((AtomicOrder)success_order_val->data.x_enum.tag);
436 LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering((AtomicOrder)failure_order_val->data.x_enum.tag);
437
438 LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,
439 success_order, failure_order, "");
440
441 return LLVMBuildExtractValue(g->builder, result_val, 1, "");
442}
443
404static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {444static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
405 assert(node->type == NodeTypeFnCallExpr);445 assert(node->type == NodeTypeFnCallExpr);
406 AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;446 AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
...@@ -546,6 +586,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {...@@ -546,6 +586,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
546 case BuiltinFnIdBreakpoint:586 case BuiltinFnIdBreakpoint:
547 set_debug_source_node(g, node);587 set_debug_source_node(g, node);
548 return LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");588 return LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
589 case BuiltinFnIdCmpExchange:
590 return gen_cmp_exchange(g, node);
549 }591 }
550 zig_unreachable();592 zig_unreachable();
551}593}
...@@ -4052,6 +4094,7 @@ static void define_builtin_types(CodeGen *g) {...@@ -4052,6 +4094,7 @@ static void define_builtin_types(CodeGen *g) {
4052 ZigLLVM_EnvironmentType environ_type = get_target_environ(i);4094 ZigLLVM_EnvironmentType environ_type = get_target_environ(i);
4053 type_enum_field->name = buf_create_from_str(ZigLLVMGetEnvironmentTypeName(environ_type));4095 type_enum_field->name = buf_create_from_str(ZigLLVMGetEnvironmentTypeName(environ_type));
4054 type_enum_field->value = i;4096 type_enum_field->value = i;
4097 type_enum_field->type_entry = g->builtin_types.entry_void;
40554098
4056 if (environ_type == g->zig_target.env_type) {4099 if (environ_type == g->zig_target.env_type) {
4057 g->target_environ_index = i;4100 g->target_environ_index = i;
...@@ -4064,6 +4107,41 @@ static void define_builtin_types(CodeGen *g) {...@@ -4064,6 +4107,41 @@ static void define_builtin_types(CodeGen *g) {
40644107
4065 g->builtin_types.entry_environ_enum = entry;4108 g->builtin_types.entry_environ_enum = entry;
4066 }4109 }
4110
4111 {
4112 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
4113 entry->deep_const = true;
4114 buf_init_from_str(&entry->name, "AtomicOrder");
4115 uint32_t field_count = 6;
4116 entry->data.enumeration.field_count = field_count;
4117 entry->data.enumeration.fields = allocate<TypeEnumField>(field_count);
4118 entry->data.enumeration.fields[0].name = buf_create_from_str("Unordered");
4119 entry->data.enumeration.fields[0].value = AtomicOrderUnordered;
4120 entry->data.enumeration.fields[0].type_entry = g->builtin_types.entry_void;
4121 entry->data.enumeration.fields[1].name = buf_create_from_str("Monotonic");
4122 entry->data.enumeration.fields[1].value = AtomicOrderMonotonic;
4123 entry->data.enumeration.fields[1].type_entry = g->builtin_types.entry_void;
4124 entry->data.enumeration.fields[2].name = buf_create_from_str("Acquire");
4125 entry->data.enumeration.fields[2].value = AtomicOrderAcquire;
4126 entry->data.enumeration.fields[2].type_entry = g->builtin_types.entry_void;
4127 entry->data.enumeration.fields[3].name = buf_create_from_str("Release");
4128 entry->data.enumeration.fields[3].value = AtomicOrderRelease;
4129 entry->data.enumeration.fields[3].type_entry = g->builtin_types.entry_void;
4130 entry->data.enumeration.fields[4].name = buf_create_from_str("AcqRel");
4131 entry->data.enumeration.fields[4].value = AtomicOrderAcqRel;
4132 entry->data.enumeration.fields[4].type_entry = g->builtin_types.entry_void;
4133 entry->data.enumeration.fields[5].name = buf_create_from_str("SeqCst");
4134 entry->data.enumeration.fields[5].value = AtomicOrderSeqCst;
4135 entry->data.enumeration.fields[5].type_entry = g->builtin_types.entry_void;
4136
4137 entry->data.enumeration.complete = true;
4138
4139 TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);
4140 entry->data.enumeration.tag_type = tag_type_entry;
4141
4142 g->builtin_types.entry_mem_order_enum = entry;
4143 g->primitive_type_table.put(&entry->name, entry);
4144 }
4067}4145}
40684146
40694147
...@@ -4162,6 +4240,8 @@ static void define_builtin_fns(CodeGen *g) {...@@ -4162,6 +4240,8 @@ static void define_builtin_fns(CodeGen *g) {
4162 create_builtin_fn_with_arg_count(g, BuiltinFnIdCImport, "c_import", 1);4240 create_builtin_fn_with_arg_count(g, BuiltinFnIdCImport, "c_import", 1);
4163 create_builtin_fn_with_arg_count(g, BuiltinFnIdErrName, "err_name", 1);4241 create_builtin_fn_with_arg_count(g, BuiltinFnIdErrName, "err_name", 1);
4164 create_builtin_fn_with_arg_count(g, BuiltinFnIdEmbedFile, "embed_file", 1);4242 create_builtin_fn_with_arg_count(g, BuiltinFnIdEmbedFile, "embed_file", 1);
4243 create_builtin_fn_with_arg_count(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);
4244 //create_builtin_fn_with_arg_count(g, BuiltinFnIdAtomicRmw, "atomicrmw", 1);
4165}4245}
41664246
4167static void init(CodeGen *g, Buf *source_path) {4247static void init(CodeGen *g, Buf *source_path) {
src/eval.cpp+1
...@@ -704,6 +704,7 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_...@@ -704,6 +704,7 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_
704 case BuiltinFnIdCImport:704 case BuiltinFnIdCImport:
705 case BuiltinFnIdErrName:705 case BuiltinFnIdErrName:
706 case BuiltinFnIdEmbedFile:706 case BuiltinFnIdEmbedFile:
707 case BuiltinFnIdCmpExchange:
707 zig_panic("TODO");708 zig_panic("TODO");
708 case BuiltinFnIdBreakpoint:709 case BuiltinFnIdBreakpoint:
709 case BuiltinFnIdInvalid:710 case BuiltinFnIdInvalid:
src/zig_llvm.cpp+25
...@@ -645,6 +645,31 @@ unsigned ZigLLVMGetPrefTypeAlignment(LLVMTargetDataRef TD, LLVMTypeRef Ty) {...@@ -645,6 +645,31 @@ unsigned ZigLLVMGetPrefTypeAlignment(LLVMTargetDataRef TD, LLVMTypeRef Ty) {
645 return unwrap(TD)->getPrefTypeAlignment(unwrap(Ty));645 return unwrap(TD)->getPrefTypeAlignment(unwrap(Ty));
646}646}
647647
648
649static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) {
650 switch (Ordering) {
651 case LLVMAtomicOrderingNotAtomic: return NotAtomic;
652 case LLVMAtomicOrderingUnordered: return Unordered;
653 case LLVMAtomicOrderingMonotonic: return Monotonic;
654 case LLVMAtomicOrderingAcquire: return Acquire;
655 case LLVMAtomicOrderingRelease: return Release;
656 case LLVMAtomicOrderingAcquireRelease: return AcquireRelease;
657 case LLVMAtomicOrderingSequentiallyConsistent: return SequentiallyConsistent;
658 }
659 abort();
660}
661
662LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
663 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
664 LLVMAtomicOrdering failure_ordering,
665 const char *name)
666{
667 return wrap(unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), unwrap(new_val),
668 mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering),
669 CrossThread));
670}
671
672
648//------------------------------------673//------------------------------------
649674
650#include "buffer.hpp"675#include "buffer.hpp"
src/zig_llvm.hpp+5
...@@ -39,6 +39,11 @@ void LLVMZigOptimizeModule(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef...@@ -39,6 +39,11 @@ void LLVMZigOptimizeModule(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef
39LLVMValueRef LLVMZigBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,39LLVMValueRef LLVMZigBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
40 unsigned NumArgs, unsigned CC, const char *Name);40 unsigned NumArgs, unsigned CC, const char *Name);
4141
42LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
43 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
44 LLVMAtomicOrdering failure_ordering,
45 const char *name);
46
42// 0 is return value, 1 is first arg47// 0 is return value, 1 is first arg
43void LLVMZigAddNonNullAttr(LLVMValueRef fn, unsigned i);48void LLVMZigAddNonNullAttr(LLVMValueRef fn, unsigned i);
4449
test/run_tests.cpp+12
...@@ -1295,8 +1295,20 @@ fn foo() {...@@ -1295,8 +1295,20 @@ fn foo() {
1295#static_eval_enable(false)1295#static_eval_enable(false)
1296fn bar() -> i32 { 2 }1296fn bar() -> i32 { 2 }
1297 )SOURCE", 1, ".tmp_source.zig:3:15: error: unable to infer expression type");1297 )SOURCE", 1, ".tmp_source.zig:3:15: error: unable to infer expression type");
1298
1299 add_compile_fail_case("atomic orderings of cmpxchg", R"SOURCE(
1300fn f() {
1301 var x: i32 = 1234;
1302 while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {}
1303 while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {}
1304}
1305 )SOURCE", 2,
1306 ".tmp_source.zig:4:72: error: failure atomic ordering must be no stricter than success",
1307 ".tmp_source.zig:5:49: error: success atomic ordering must be Monotonic or stricter");
1298}1308}
12991309
1310//////////////////////////////////////////////////////////////////////////////
1311
1300static void add_debug_safety_test_cases(void) {1312static void add_debug_safety_test_cases(void) {
1301 add_debug_safety_case("out of bounds slice access", R"SOURCE(1313 add_debug_safety_case("out of bounds slice access", R"SOURCE(
1302pub fn main(args: [][]u8) -> %void {1314pub fn main(args: [][]u8) -> %void {
test/self_hosted.zig+7
...@@ -1442,3 +1442,10 @@ fn assign_to_if_var_ptr() {...@@ -1442,3 +1442,10 @@ fn assign_to_if_var_ptr() {
14421442
1443 assert(??maybe_bool == false);1443 assert(??maybe_bool == false);
1444}1444}
1445
1446#attribute("test")
1447fn cmpxchg() {
1448 var x: i32 = 1234;
1449 while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {}
1450 assert(x == 5678);
1451}