| author | |
| committer | |
| log | c95e49785772a54916418630fbc7283791a4aa72 |
| tree | 4097d9cfef1b2ce5209410310480fad730f5a5ec |
| parent | f2bc5ccbc2f322b625719d374bbb09a4335c1bd1 |
8 files changed, 213 insertions(+), 0 deletions(-)
src/all_types.hpp+10| ... | ... | @@ -1115,6 +1115,7 @@ enum BuiltinFnId { |
| 1115 | 1115 | BuiltinFnIdErrName, |
| 1116 | 1116 | BuiltinFnIdBreakpoint, |
| 1117 | 1117 | BuiltinFnIdEmbedFile, |
| 1118 | BuiltinFnIdCmpExchange, | |
| 1118 | 1119 | }; |
| 1119 | 1120 | |
| 1120 | 1121 | struct BuiltinFnEntry { |
| ... | ... | @@ -1183,6 +1184,7 @@ struct CodeGen { |
| 1183 | 1184 | TypeTableEntry *entry_os_enum; |
| 1184 | 1185 | TypeTableEntry *entry_arch_enum; |
| 1185 | 1186 | TypeTableEntry *entry_environ_enum; |
| 1187 | TypeTableEntry *entry_mem_order_enum; | |
| 1186 | 1188 | } builtin_types; |
| 1187 | 1189 | |
| 1188 | 1190 | ZigTarget zig_target; |
| ... | ... | @@ -1322,6 +1324,14 @@ struct BlockContext { |
| 1322 | 1324 | bool safety_off; |
| 1323 | 1325 | }; |
| 1324 | 1326 | |
| 1327 | enum AtomicOrder { | |
| 1328 | AtomicOrderUnordered, | |
| 1329 | AtomicOrderMonotonic, | |
| 1330 | AtomicOrderAcquire, | |
| 1331 | AtomicOrderRelease, | |
| 1332 | AtomicOrderAcqRel, | |
| 1333 | AtomicOrderSeqCst, | |
| 1334 | }; | |
| 1325 | 1335 | |
| 1326 | 1336 | |
| 1327 | 1337 | #endif |
src/analyze.cpp+73| ... | ... | @@ -4414,6 +4414,77 @@ static TypeTableEntry *analyze_embed_file(CodeGen *g, ImportTableEntry *import, |
| 4414 | 4414 | return resolve_expr_const_val_as_string_lit(g, node, &file_contents); |
| 4415 | 4415 | } |
| 4416 | 4416 | |
| 4417 | static 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 | ||
| 4417 | 4488 | static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 4418 | 4489 | TypeTableEntry *expected_type, AstNode *node) |
| 4419 | 4490 | { |
| ... | ... | @@ -4750,6 +4821,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 4750 | 4821 | return g->builtin_types.entry_void; |
| 4751 | 4822 | case BuiltinFnIdEmbedFile: |
| 4752 | 4823 | return analyze_embed_file(g, import, context, node); |
| 4824 | case BuiltinFnIdCmpExchange: | |
| 4825 | return analyze_cmpxchg(g, import, context, node); | |
| 4753 | 4826 | } |
| 4754 | 4827 | zig_unreachable(); |
| 4755 | 4828 | } |
src/codegen.cpp+80| ... | ... | @@ -401,6 +401,46 @@ static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) { |
| 401 | 401 | return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, ""); |
| 402 | 402 | } |
| 403 | 403 | |
| 404 | static 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 | ||
| 416 | static 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 | ||
| 404 | 444 | static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { |
| 405 | 445 | assert(node->type == NodeTypeFnCallExpr); |
| 406 | 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 | 586 | case BuiltinFnIdBreakpoint: |
| 547 | 587 | set_debug_source_node(g, node); |
| 548 | 588 | return LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); |
| 589 | case BuiltinFnIdCmpExchange: | |
| 590 | return gen_cmp_exchange(g, node); | |
| 549 | 591 | } |
| 550 | 592 | zig_unreachable(); |
| 551 | 593 | } |
| ... | ... | @@ -4052,6 +4094,7 @@ static void define_builtin_types(CodeGen *g) { |
| 4052 | 4094 | ZigLLVM_EnvironmentType environ_type = get_target_environ(i); |
| 4053 | 4095 | type_enum_field->name = buf_create_from_str(ZigLLVMGetEnvironmentTypeName(environ_type)); |
| 4054 | 4096 | type_enum_field->value = i; |
| 4097 | type_enum_field->type_entry = g->builtin_types.entry_void; | |
| 4055 | 4098 | |
| 4056 | 4099 | if (environ_type == g->zig_target.env_type) { |
| 4057 | 4100 | g->target_environ_index = i; |
| ... | ... | @@ -4064,6 +4107,41 @@ static void define_builtin_types(CodeGen *g) { |
| 4064 | 4107 | |
| 4065 | 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 | } |
| 4068 | 4146 | |
| 4069 | 4147 | |
| ... | ... | @@ -4162,6 +4240,8 @@ static void define_builtin_fns(CodeGen *g) { |
| 4162 | 4240 | create_builtin_fn_with_arg_count(g, BuiltinFnIdCImport, "c_import", 1); |
| 4163 | 4241 | create_builtin_fn_with_arg_count(g, BuiltinFnIdErrName, "err_name", 1); |
| 4164 | 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 | } |
| 4166 | 4246 | |
| 4167 | 4247 | static 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 | 704 | case BuiltinFnIdCImport: |
| 705 | 705 | case BuiltinFnIdErrName: |
| 706 | 706 | case BuiltinFnIdEmbedFile: |
| 707 | case BuiltinFnIdCmpExchange: | |
| 707 | 708 | zig_panic("TODO"); |
| 708 | 709 | case BuiltinFnIdBreakpoint: |
| 709 | 710 | case BuiltinFnIdInvalid: |
src/zig_llvm.cpp+25| ... | ... | @@ -645,6 +645,31 @@ unsigned ZigLLVMGetPrefTypeAlignment(LLVMTargetDataRef TD, LLVMTypeRef Ty) { |
| 645 | 645 | return unwrap(TD)->getPrefTypeAlignment(unwrap(Ty)); |
| 646 | 646 | } |
| 647 | 647 | |
| 648 | ||
| 649 | static 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 | ||
| 662 | LLVMValueRef 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 | //------------------------------------ |
| 649 | 674 | |
| 650 | 675 | #include "buffer.hpp" |
src/zig_llvm.hpp+5| ... | ... | @@ -39,6 +39,11 @@ void LLVMZigOptimizeModule(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef |
| 39 | 39 | LLVMValueRef LLVMZigBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, |
| 40 | 40 | unsigned NumArgs, unsigned CC, const char *Name); |
| 41 | 41 | |
| 42 | LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp, | |
| 43 | LLVMValueRef new_val, LLVMAtomicOrdering success_ordering, | |
| 44 | LLVMAtomicOrdering failure_ordering, | |
| 45 | const char *name); | |
| 46 | ||
| 42 | 47 | // 0 is return value, 1 is first arg |
| 43 | 48 | void LLVMZigAddNonNullAttr(LLVMValueRef fn, unsigned i); |
| 44 | 49 |
test/run_tests.cpp+12| ... | ... | @@ -1295,8 +1295,20 @@ fn foo() { |
| 1295 | 1295 | #static_eval_enable(false) |
| 1296 | 1296 | fn bar() -> i32 { 2 } |
| 1297 | 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( | |
| 1300 | fn 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 | } |
| 1299 | 1309 | |
| 1310 | ////////////////////////////////////////////////////////////////////////////// | |
| 1311 | ||
| 1300 | 1312 | static void add_debug_safety_test_cases(void) { |
| 1301 | 1313 | add_debug_safety_case("out of bounds slice access", R"SOURCE( |
| 1302 | 1314 | pub fn main(args: [][]u8) -> %void { |
test/self_hosted.zig+7| ... | ... | @@ -1442,3 +1442,10 @@ fn assign_to_if_var_ptr() { |
| 1442 | 1442 | |
| 1443 | 1443 | assert(??maybe_bool == false); |
| 1444 | 1444 | } |
| 1445 | ||
| 1446 | #attribute("test") | |
| 1447 | fn cmpxchg() { | |
| 1448 | var x: i32 = 1234; | |
| 1449 | while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} | |
| 1450 | assert(x == 5678); | |
| 1451 | } |