Commit 8fcb1a141b

Andrew Kelley <superjoe30@gmail.com>
2016-12-11 10:06:07
IR: implement fence and cmpxchg builtins
1 parent 10cea15
src/all_types.hpp
@@ -1407,6 +1407,8 @@ enum IrInstructionId {
     IrInstructionIdCompileErr,
     IrInstructionIdErrName,
     IrInstructionIdEmbedFile,
+    IrInstructionIdCmpxchg,
+    IrInstructionIdFence,
 };
 
 struct IrInstruction {
@@ -1859,6 +1861,29 @@ struct IrInstructionEmbedFile {
     IrInstruction *name;
 };
 
+struct IrInstructionCmpxchg {
+    IrInstruction base;
+
+    IrInstruction *ptr;
+    IrInstruction *cmp_value;
+    IrInstruction *new_value;
+    IrInstruction *success_order_value;
+    IrInstruction *failure_order_value;
+
+    // if this instruction gets to runtime then we know these values:
+    AtomicOrder success_order;
+    AtomicOrder failure_order;
+};
+
+struct IrInstructionFence {
+    IrInstruction base;
+
+    IrInstruction *order_value;
+
+    // if this instruction gets to runtime then we know these values:
+    AtomicOrder order;
+};
+
 enum LValPurpose {
     LValPurposeNone,
     LValPurposeAssign,
src/analyze.cpp
@@ -2798,3 +2798,14 @@ ConstExprValue *create_const_float(double value) {
     init_const_float(const_val, value);
     return const_val;
 }
+
+void init_const_enum_tag(ConstExprValue *const_val, uint64_t tag) {
+    const_val->special = ConstValSpecialStatic;
+    const_val->data.x_enum.tag = tag;
+}
+
+ConstExprValue *create_const_enum_tag(uint64_t tag) {
+    ConstExprValue *const_val = allocate<ConstExprValue>(1);
+    init_const_enum_tag(const_val, tag);
+    return const_val;
+}
src/analyze.hpp
@@ -95,4 +95,7 @@ ConstExprValue *create_const_signed(int64_t x);
 void init_const_float(ConstExprValue *const_val, double value);
 ConstExprValue *create_const_float(double value);
 
+void init_const_enum_tag(ConstExprValue *const_val, uint64_t tag);
+ConstExprValue *create_const_enum_tag(uint64_t tag);
+
 #endif
src/codegen.cpp
@@ -1839,6 +1839,38 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI
     return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, "");
 }
 
+static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
+    switch (atomic_order) {
+        case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered;
+        case AtomicOrderMonotonic: return LLVMAtomicOrderingMonotonic;
+        case AtomicOrderAcquire: return LLVMAtomicOrderingAcquire;
+        case AtomicOrderRelease: return LLVMAtomicOrderingRelease;
+        case AtomicOrderAcqRel: return LLVMAtomicOrderingAcquireRelease;
+        case AtomicOrderSeqCst: return LLVMAtomicOrderingSequentiallyConsistent;
+    }
+    zig_unreachable();
+}
+
+static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchg *instruction) {
+    LLVMValueRef ptr_val = ir_llvm_value(g, instruction->ptr);
+    LLVMValueRef cmp_val = ir_llvm_value(g, instruction->cmp_value);
+    LLVMValueRef new_val = ir_llvm_value(g, instruction->new_value);
+
+    LLVMAtomicOrdering success_order = to_LLVMAtomicOrdering(instruction->success_order);
+    LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order);
+
+    LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,
+            success_order, failure_order);
+
+    return LLVMBuildExtractValue(g->builder, result_val, 1, "");
+}
+
+static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) {
+    LLVMAtomicOrdering atomic_order = to_LLVMAtomicOrdering(instruction->order);
+    LLVMBuildFence(g->builder, atomic_order, false, "");
+    return nullptr;
+}
+
 static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
     AstNode *source_node = instruction->source_node;
     Scope *scope = instruction->scope;
@@ -1928,6 +1960,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_ref(g, executable, (IrInstructionRef *)instruction);
         case IrInstructionIdErrName:
             return ir_render_err_name(g, executable, (IrInstructionErrName *)instruction);
+        case IrInstructionIdCmpxchg:
+            return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchg *)instruction);
+        case IrInstructionIdFence:
+            return ir_render_fence(g, executable, (IrInstructionFence *)instruction);
         case IrInstructionIdSwitchVar:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdStructInit:
@@ -2989,6 +3025,7 @@ static void define_builtin_types(CodeGen *g) {
 
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
+        entry->zero_bits = true; // only allowed at compile time
         buf_init_from_str(&entry->name, "AtomicOrder");
         uint32_t field_count = 6;
         entry->data.enumeration.src_field_count = field_count;
src/ir.cpp
@@ -347,6 +347,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEmbedFile *) {
     return IrInstructionIdEmbedFile;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchg *) {
+    return IrInstructionIdCmpxchg;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) {
+    return IrInstructionIdFence;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -1357,6 +1365,54 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr,
+    IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value,
+    AtomicOrder success_order, AtomicOrder failure_order)
+{
+    IrInstructionCmpxchg *instruction = ir_build_instruction<IrInstructionCmpxchg>(irb, scope, source_node);
+    instruction->ptr = ptr;
+    instruction->cmp_value = cmp_value;
+    instruction->new_value = new_value;
+    instruction->success_order_value = success_order_value;
+    instruction->failure_order_value = failure_order_value;
+    instruction->success_order = success_order;
+    instruction->failure_order = failure_order;
+
+    ir_ref_instruction(ptr);
+    ir_ref_instruction(cmp_value);
+    ir_ref_instruction(new_value);
+    ir_ref_instruction(success_order_value);
+    ir_ref_instruction(failure_order_value);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_cmpxchg_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr,
+    IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value,
+    AtomicOrder success_order, AtomicOrder failure_order)
+{
+    IrInstruction *new_instruction = ir_build_cmpxchg(irb, old_instruction->scope, old_instruction->source_node,
+        ptr, cmp_value, new_value, success_order_value, failure_order_value, success_order, failure_order);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
+static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) {
+    IrInstructionFence *instruction = ir_build_instruction<IrInstructionFence>(irb, scope, source_node);
+    instruction->order_value = order_value;
+    instruction->order = order;
+
+    ir_ref_instruction(order_value);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_fence_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *order_value, AtomicOrder order) {
+    IrInstruction *new_instruction = ir_build_fence(irb, old_instruction->scope, old_instruction->source_node, order_value, order);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
 static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope,
         bool gen_error_defers, bool gen_maybe_defers)
 {
@@ -2096,6 +2152,46 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
 
                 return ir_build_embed_file(irb, scope, node, arg0_value);
             }
+        case BuiltinFnIdCmpExchange:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_instruction)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_instruction)
+                    return arg1_value;
+
+                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
+                IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
+                if (arg2_value == irb->codegen->invalid_instruction)
+                    return arg2_value;
+
+                AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
+                IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope);
+                if (arg3_value == irb->codegen->invalid_instruction)
+                    return arg3_value;
+
+                AstNode *arg4_node = node->data.fn_call_expr.params.at(4);
+                IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope);
+                if (arg4_value == irb->codegen->invalid_instruction)
+                    return arg4_value;
+
+                return ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value,
+                    arg2_value, arg3_value, arg4_value,
+                    AtomicOrderUnordered, AtomicOrderUnordered);
+            }
+        case BuiltinFnIdFence:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_instruction)
+                    return arg0_value;
+
+                return ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered);
+            }
         case BuiltinFnIdMemcpy:
         case BuiltinFnIdMemset:
         case BuiltinFnIdAlignof:
@@ -2107,8 +2203,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
         case BuiltinFnIdBreakpoint:
         case BuiltinFnIdReturnAddress:
         case BuiltinFnIdFrameAddress:
-        case BuiltinFnIdCmpExchange:
-        case BuiltinFnIdFence:
         case BuiltinFnIdDivExact:
         case BuiltinFnIdTruncate:
         case BuiltinFnIdIntType:
@@ -3470,6 +3564,12 @@ static bool is_slice(TypeTableEntry *type) {
     return type->id == TypeTableEntryIdStruct && type->data.structure.is_slice;
 }
 
+static bool is_container(TypeTableEntry *type) {
+    return type->id == TypeTableEntryIdStruct ||
+        type->id == TypeTableEntryIdEnum ||
+        type->id == TypeTableEntryIdUnion;
+}
+
 static bool is_u8(TypeTableEntry *type) {
     return type->id == TypeTableEntryIdInt &&
         !type->data.integral.is_signed && type->data.integral.bit_count == 8;
@@ -4085,6 +4185,22 @@ static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *value, bool *out) {
     return true;
 }
 
+static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) {
+    if (value->type_entry->id == TypeTableEntryIdInvalid)
+        return false;
+
+    IrInstruction *casted_value = ir_get_casted_value(ira, value, ira->codegen->builtin_types.entry_atomic_order_enum);
+    if (casted_value->type_entry->id == TypeTableEntryIdInvalid)
+        return false;
+
+    ConstExprValue *const_val = ir_resolve_const(ira, casted_value);
+    if (!const_val)
+        return false;
+
+    *out = (AtomicOrder)const_val->data.x_enum.tag;
+    return true;
+}
+
 static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
     if (value->type_entry->id == TypeTableEntryIdInvalid)
         return nullptr;
@@ -5689,15 +5805,23 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
     if (container_ptr->type_entry->id == TypeTableEntryIdInvalid)
         return ira->codegen->builtin_types.entry_invalid;
 
-    assert(container_ptr->type_entry->id == TypeTableEntryIdPointer);
-    TypeTableEntry *container_type = container_ptr->type_entry->data.pointer.child_type;
+    TypeTableEntry *container_type;
+    if (container_ptr->type_entry->id == TypeTableEntryIdPointer) {
+        container_type = container_ptr->type_entry->data.pointer.child_type;
+    } else if (container_ptr->type_entry->id == TypeTableEntryIdMetaType) {
+        container_type = container_ptr->type_entry;
+    } else {
+        zig_unreachable();
+    }
 
+    bool depends_on_compile_var = container_ptr->static_value.depends_on_compile_var;
     Buf *field_name = field_ptr_instruction->field_name;
     AstNode *source_node = field_ptr_instruction->base.source_node;
 
     if (container_type->id == TypeTableEntryIdInvalid) {
         return container_type;
     } else if (is_container_ref(container_type)) {
+        assert(container_ptr->type_entry->id == TypeTableEntryIdPointer);
         return ir_analyze_container_field_ptr(ira, field_name, field_ptr_instruction, container_ptr, container_type);
     } else if (container_type->id == TypeTableEntryIdArray) {
         if (buf_eql_str(field_name, "len")) {
@@ -5717,26 +5841,43 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
         ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr);
         if (!container_ptr_val)
             return ira->codegen->builtin_types.entry_invalid;
-        ConstExprValue *child_val = const_ptr_pointee(container_ptr_val);
-        TypeTableEntry *child_type = child_val->data.x_type;
+
+        TypeTableEntry *child_type;
+        if (container_ptr->type_entry->id == TypeTableEntryIdMetaType) {
+            TypeTableEntry *ptr_type = container_ptr_val->data.x_type;
+            assert(ptr_type->id == TypeTableEntryIdPointer);
+            child_type = ptr_type->data.pointer.child_type;
+        } else if (container_ptr->type_entry->id == TypeTableEntryIdPointer) {
+            ConstExprValue *child_val = const_ptr_pointee(container_ptr_val);
+            child_type = child_val->data.x_type;
+        } else {
+            zig_unreachable();
+        }
 
         if (child_type->id == TypeTableEntryIdInvalid) {
             return ira->codegen->builtin_types.entry_invalid;
-        } else if (child_type->id == TypeTableEntryIdEnum) {
-            zig_panic("TODO enum type field");
-        } else if (child_type->id == TypeTableEntryIdStruct) {
+        } else if (is_container(child_type)) {
+            if (child_type->id == TypeTableEntryIdEnum) {
+                TypeEnumField *field = find_enum_type_field(child_type, field_name);
+                if (field) {
+                    if (field->type_entry->id == TypeTableEntryIdVoid) {
+                        return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_enum_tag(field->value),
+                                child_type, depends_on_compile_var, ConstPtrSpecialNone);
+                    } else {
+                        zig_panic("TODO enum tag type");
+                    }
+                }
+            }
             ScopeDecls *container_scope = get_container_scope(child_type);
             auto entry = container_scope->decl_table.maybe_get(field_name);
             Tld *tld = entry ? entry->value : nullptr;
             if (tld) {
-                bool depends_on_compile_var = container_ptr->static_value.depends_on_compile_var;
                 return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld, depends_on_compile_var);
-            } else {
-                add_node_error(ira->codegen, source_node,
-                    buf_sprintf("container '%s' has no member called '%s'",
-                        buf_ptr(&child_type->name), buf_ptr(field_name)));
-                return ira->codegen->builtin_types.entry_invalid;
             }
+            ir_add_error(ira, &field_ptr_instruction->base,
+                buf_sprintf("container '%s' has no member called '%s'",
+                    buf_ptr(&child_type->name), buf_ptr(field_name)));
+            return ira->codegen->builtin_types.entry_invalid;
         } else if (child_type->id == TypeTableEntryIdPureError) {
             auto err_table_entry = ira->codegen->error_table.maybe_get(field_name);
             if (err_table_entry) {
@@ -5744,7 +5885,6 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
                 const_val->special = ConstValSpecialStatic;
                 const_val->data.x_pure_err = err_table_entry->value;
 
-                bool depends_on_compile_var = container_ptr->static_value.depends_on_compile_var;
                 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val,
                         child_type, depends_on_compile_var, ConstPtrSpecialNone);
             }
@@ -5760,6 +5900,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
             return ira->codegen->builtin_types.entry_invalid;
         }
     } else if (container_type->id == TypeTableEntryIdNamespace) {
+        assert(container_ptr->type_entry->id == TypeTableEntryIdPointer);
         ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr);
         if (!container_ptr_val)
             return ira->codegen->builtin_types.entry_invalid;
@@ -5769,7 +5910,6 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
 
         ImportTableEntry *namespace_import = namespace_val->data.x_import;
 
-        bool depends_on_compile_var = container_ptr->static_value.depends_on_compile_var;
         Tld *tld = find_decl(&namespace_import->decls_scope->base, field_name);
         if (!tld) {
             // we must now resolve all the use decls
@@ -7184,10 +7324,10 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr
     int err;
     if ((err = os_fetch_file_path(&file_path, &file_contents))) {
         if (err == ErrorFileNotFound) {
-            ir_add_error(ira, &instruction->base, buf_sprintf("unable to find '%s'", buf_ptr(&file_path)));
+            ir_add_error(ira, instruction->name, buf_sprintf("unable to find '%s'", buf_ptr(&file_path)));
             return ira->codegen->builtin_types.entry_invalid;
         } else {
-            ir_add_error(ira, &instruction->base, buf_sprintf("unable to open '%s': %s", buf_ptr(&file_path), err_str(err)));
+            ir_add_error(ira, instruction->name, buf_sprintf("unable to open '%s': %s", buf_ptr(&file_path), err_str(err)));
             return ira->codegen->builtin_types.entry_invalid;
         }
     }
@@ -7197,11 +7337,94 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr
 
     bool depends_on_compile_var = true;
     ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
-    init_const_str_lit(out_val,&file_contents);
+    init_const_str_lit(out_val, &file_contents);
 
     return get_array_type(ira->codegen, ira->codegen->builtin_types.entry_u8, buf_len(&file_contents));
 }
 
+static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) {
+    IrInstruction *ptr = instruction->ptr->other;
+    if (ptr->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    IrInstruction *cmp_value = instruction->cmp_value->other;
+    if (cmp_value->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    IrInstruction *new_value = instruction->new_value->other;
+    if (new_value->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    IrInstruction *success_order_value = instruction->success_order_value->other;
+    if (success_order_value->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    AtomicOrder success_order;
+    if (!ir_resolve_atomic_order(ira, success_order_value, &success_order))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    IrInstruction *failure_order_value = instruction->failure_order_value->other;
+    if (failure_order_value->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    AtomicOrder failure_order;
+    if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    if (ptr->type_entry->id != TypeTableEntryIdPointer) {
+        ir_add_error(ira, instruction->ptr,
+            buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&ptr->type_entry->name)));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+
+    TypeTableEntry *child_type = ptr->type_entry->data.pointer.child_type;
+
+    IrInstruction *casted_cmp_value = ir_get_casted_value(ira, cmp_value, child_type);
+    if (casted_cmp_value->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    IrInstruction *casted_new_value = ir_get_casted_value(ira, new_value, child_type);
+    if (casted_new_value->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    if (success_order < AtomicOrderMonotonic) {
+        ir_add_error(ira, success_order_value,
+                buf_sprintf("success atomic ordering must be Monotonic or stricter"));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+    if (failure_order < AtomicOrderMonotonic) {
+        ir_add_error(ira, failure_order_value,
+                buf_sprintf("failure atomic ordering must be Monotonic or stricter"));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+    if (failure_order > success_order) {
+        ir_add_error(ira, failure_order_value,
+                buf_sprintf("failure atomic ordering must be no stricter than success"));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+    if (failure_order == AtomicOrderRelease || failure_order == AtomicOrderAcqRel) {
+        ir_add_error(ira, failure_order_value,
+                buf_sprintf("failure atomic ordering must not be Release or AcqRel"));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+
+    ir_build_cmpxchg_from(&ira->new_irb, &instruction->base, ptr, casted_cmp_value, casted_new_value,
+        success_order_value, failure_order_value, success_order, failure_order);
+    return ira->codegen->builtin_types.entry_bool;
+}
+
+static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) {
+    IrInstruction *order_value = instruction->order_value->other;
+    if (order_value->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    AtomicOrder order;
+    if (!ir_resolve_atomic_order(ira, order_value, &order))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    ir_build_fence_from(&ira->new_irb, &instruction->base, order_value, order);
+    return ira->codegen->builtin_types.entry_void;
+}
 
 static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
     switch (instruction->id) {
@@ -7305,6 +7528,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_c_undef(ira, (IrInstructionCUndef *)instruction);
         case IrInstructionIdEmbedFile:
             return ir_analyze_instruction_embed_file(ira, (IrInstructionEmbedFile *)instruction);
+        case IrInstructionIdCmpxchg:
+            return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)instruction);
+        case IrInstructionIdFence:
+            return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction);
         case IrInstructionIdCast:
         case IrInstructionIdStructFieldPtr:
         case IrInstructionIdEnumFieldPtr:
@@ -7404,6 +7631,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdCInclude:
         case IrInstructionIdCDefine:
         case IrInstructionIdCUndef:
+        case IrInstructionIdCmpxchg:
+        case IrInstructionIdFence:
             return true;
         case IrInstructionIdPhi:
         case IrInstructionIdUnOp:
@@ -7453,102 +7682,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 // TODO port over all this commented out code into new IR way of doing things
 
 
-//static TypeTableEntry *analyze_cmpxchg(CodeGen *g, ImportTableEntry *import,
-//        BlockContext *context, AstNode *node)
-//{
-//    assert(node->type == NodeTypeFnCallExpr);
-//
-//    AstNode **ptr_arg = &node->data.fn_call_expr.params.at(0);
-//    AstNode **cmp_arg = &node->data.fn_call_expr.params.at(1);
-//    AstNode **new_arg = &node->data.fn_call_expr.params.at(2);
-//    AstNode **success_order_arg = &node->data.fn_call_expr.params.at(3);
-//    AstNode **failure_order_arg = &node->data.fn_call_expr.params.at(4);
-//
-//    TypeTableEntry *ptr_type = analyze_expression(g, import, context, nullptr, *ptr_arg);
-//    if (ptr_type->id == TypeTableEntryIdInvalid) {
-//        return g->builtin_types.entry_invalid;
-//    } else if (ptr_type->id != TypeTableEntryIdPointer) {
-//        add_node_error(g, *ptr_arg,
-//            buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&ptr_type->name)));
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
-//    TypeTableEntry *cmp_type = analyze_expression(g, import, context, child_type, *cmp_arg);
-//    TypeTableEntry *new_type = analyze_expression(g, import, context, child_type, *new_arg);
-//
-//    TypeTableEntry *success_order_type = analyze_expression(g, import, context,
-//            g->builtin_types.entry_atomic_order_enum, *success_order_arg);
-//    TypeTableEntry *failure_order_type = analyze_expression(g, import, context,
-//            g->builtin_types.entry_atomic_order_enum, *failure_order_arg);
-//
-//    if (cmp_type->id == TypeTableEntryIdInvalid ||
-//        new_type->id == TypeTableEntryIdInvalid ||
-//        success_order_type->id == TypeTableEntryIdInvalid ||
-//        failure_order_type->id == TypeTableEntryIdInvalid)
-//    {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    ConstExprValue *success_order_val = &get_resolved_expr(*success_order_arg)->const_val;
-//    ConstExprValue *failure_order_val = &get_resolved_expr(*failure_order_arg)->const_val;
-//    if (!success_order_val->ok) {
-//        add_node_error(g, *success_order_arg, buf_sprintf("unable to evaluate constant expression"));
-//        return g->builtin_types.entry_invalid;
-//    } else if (!failure_order_val->ok) {
-//        add_node_error(g, *failure_order_arg, buf_sprintf("unable to evaluate constant expression"));
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    if (success_order_val->data.x_enum.tag < AtomicOrderMonotonic) {
-//        add_node_error(g, *success_order_arg,
-//                buf_sprintf("success atomic ordering must be Monotonic or stricter"));
-//        return g->builtin_types.entry_invalid;
-//    }
-//    if (failure_order_val->data.x_enum.tag < AtomicOrderMonotonic) {
-//        add_node_error(g, *failure_order_arg,
-//                buf_sprintf("failure atomic ordering must be Monotonic or stricter"));
-//        return g->builtin_types.entry_invalid;
-//    }
-//    if (failure_order_val->data.x_enum.tag > success_order_val->data.x_enum.tag) {
-//        add_node_error(g, *failure_order_arg,
-//                buf_sprintf("failure atomic ordering must be no stricter than success"));
-//        return g->builtin_types.entry_invalid;
-//    }
-//    if (failure_order_val->data.x_enum.tag == AtomicOrderRelease ||
-//        failure_order_val->data.x_enum.tag == AtomicOrderAcqRel)
-//    {
-//        add_node_error(g, *failure_order_arg,
-//                buf_sprintf("failure atomic ordering must not be Release or AcqRel"));
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    return g->builtin_types.entry_bool;
-//}
-//
-//static TypeTableEntry *analyze_fence(CodeGen *g, ImportTableEntry *import,
-//        BlockContext *context, AstNode *node)
-//{
-//    assert(node->type == NodeTypeFnCallExpr);
-//
-//    AstNode **atomic_order_arg = &node->data.fn_call_expr.params.at(0);
-//    TypeTableEntry *atomic_order_type = analyze_expression(g, import, context,
-//            g->builtin_types.entry_atomic_order_enum, *atomic_order_arg);
-//
-//    if (atomic_order_type->id == TypeTableEntryIdInvalid) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    ConstExprValue *atomic_order_val = &get_resolved_expr(*atomic_order_arg)->const_val;
-//
-//    if (!atomic_order_val->ok) {
-//        add_node_error(g, *atomic_order_arg, buf_sprintf("unable to evaluate constant expression"));
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    return g->builtin_types.entry_void;
-//}
-//
 //static TypeTableEntry *analyze_div_exact(CodeGen *g, ImportTableEntry *import,
 //        BlockContext *context, AstNode *node)
 //{
@@ -7780,8 +7913,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 //                    return g->builtin_types.entry_invalid;
 //                }
 //            }
-//        case BuiltinFnIdImport:
-//            return analyze_import(g, import, context, node);
 //        case BuiltinFnIdBreakpoint:
 //            mark_impure_fn(g, context, node);
 //            return g->builtin_types.entry_void;
@@ -7789,20 +7920,12 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 //        case BuiltinFnIdFrameAddress:
 //            mark_impure_fn(g, context, node);
 //            return builtin_fn->return_type;
-//        case BuiltinFnIdCmpExchange:
-//            return analyze_cmpxchg(g, import, context, node);
-//        case BuiltinFnIdFence:
-//            return analyze_fence(g, import, context, node);
 //        case BuiltinFnIdDivExact:
 //            return analyze_div_exact(g, import, context, node);
 //        case BuiltinFnIdTruncate:
 //            return analyze_truncate(g, import, context, node);
 //        case BuiltinFnIdIntType:
 //            return analyze_int_type(g, import, context, node);
-//        case BuiltinFnIdSetFnTest:
-//            return analyze_set_fn_test(g, import, context, node);
-//        case BuiltinFnIdSetFnNoInline:
-//            return analyze_set_fn_no_inline(g, import, context, node);
 //    }
 //    zig_unreachable();
 //}
@@ -7881,68 +8004,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 //    }
 //    zig_unreachable();
 //}
-//static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        AstNode *field_access_node, AstNode *value_node, TypeTableEntry *enum_type, Buf *field_name,
-//        AstNode *out_node)
-//{
-//    assert(field_access_node->type == NodeTypeFieldAccessExpr);
-//
-//    TypeEnumField *type_enum_field = find_enum_type_field(enum_type, field_name);
-//    if (type_enum_field->type_entry->id == TypeTableEntryIdInvalid) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    field_access_node->data.field_access_expr.type_enum_field = type_enum_field;
-//
-//    if (type_enum_field) {
-//        if (value_node) {
-//            AstNode **value_node_ptr = value_node->parent_field;
-//            TypeTableEntry *value_type = analyze_expression(g, import, context,
-//                    type_enum_field->type_entry, value_node);
-//
-//            if (value_type->id == TypeTableEntryIdInvalid) {
-//                return g->builtin_types.entry_invalid;
-//            }
-//
-//            StructValExprCodeGen *codegen = &field_access_node->data.field_access_expr.resolved_struct_val_expr;
-//            codegen->type_entry = enum_type;
-//            codegen->source_node = field_access_node;
-//
-//            ConstExprValue *value_const_val = &get_resolved_expr(*value_node_ptr)->const_val;
-//            if (value_const_val->ok) {
-//                ConstExprValue *const_val = &get_resolved_expr(out_node)->const_val;
-//                const_val->ok = true;
-//                const_val->data.x_enum.tag = type_enum_field->value;
-//                const_val->data.x_enum.payload = value_const_val;
-//            } else {
-//                if (context->fn_entry) {
-//                    context->fn_entry->struct_val_expr_alloca_list.append(codegen);
-//                } else {
-//                    add_node_error(g, *value_node_ptr, buf_sprintf("unable to evaluate constant expression"));
-//                    return g->builtin_types.entry_invalid;
-//                }
-//            }
-//        } else if (type_enum_field->type_entry->id != TypeTableEntryIdVoid) {
-//            add_node_error(g, field_access_node,
-//                buf_sprintf("enum value '%s.%s' requires parameter of type '%s'",
-//                    buf_ptr(&enum_type->name),
-//                    buf_ptr(field_name),
-//                    buf_ptr(&type_enum_field->type_entry->name)));
-//        } else {
-//            Expr *expr = get_resolved_expr(out_node);
-//            expr->const_val.ok = true;
-//            expr->const_val.data.x_enum.tag = type_enum_field->value;
-//            expr->const_val.data.x_enum.payload = nullptr;
-//        }
-//    } else {
-//        add_node_error(g, field_access_node,
-//            buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
-//                buf_ptr(&enum_type->name)));
-//    }
-//    return enum_type;
-//}
-//
-//
 //static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
 //        AstNode *node)
 //{
@@ -8182,34 +8243,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 //
 
 
-//static LLVMValueRef gen_cmp_exchange(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeFnCallExpr);
-//
-//    AstNode *ptr_arg = node->data.fn_call_expr.params.at(0);
-//    AstNode *cmp_arg = node->data.fn_call_expr.params.at(1);
-//    AstNode *new_arg = node->data.fn_call_expr.params.at(2);
-//    AstNode *success_order_arg = node->data.fn_call_expr.params.at(3);
-//    AstNode *failure_order_arg = node->data.fn_call_expr.params.at(4);
-//
-//    LLVMValueRef ptr_val = gen_expr(g, ptr_arg);
-//    LLVMValueRef cmp_val = gen_expr(g, cmp_arg);
-//    LLVMValueRef new_val = gen_expr(g, new_arg);
-//
-//    ConstExprValue *success_order_val = &get_resolved_expr(success_order_arg)->const_val;
-//    ConstExprValue *failure_order_val = &get_resolved_expr(failure_order_arg)->const_val;
-//
-//    assert(success_order_val->ok);
-//    assert(failure_order_val->ok);
-//
-//    LLVMAtomicOrdering success_order = to_LLVMAtomicOrdering((AtomicOrder)success_order_val->data.x_enum.tag);
-//    LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering((AtomicOrder)failure_order_val->data.x_enum.tag);
-//
-//    LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,
-//            success_order, failure_order);
-//
-//    return LLVMBuildExtractValue(g->builder, result_val, 1, "");
-//}
-//
 //static LLVMValueRef gen_div_exact(CodeGen *g, AstNode *node) {
 //    assert(node->type == NodeTypeFnCallExpr);
 //
@@ -8269,9 +8302,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 //    switch (builtin_fn->id) {
 //        case BuiltinFnIdInvalid:
 //        case BuiltinFnIdTypeof:
-//        case BuiltinFnIdImport:
-//        case BuiltinFnIdCImport:
-//        case BuiltinFnIdCompileErr:
 //        case BuiltinFnIdIntType:
 //            zig_unreachable();
 //        case BuiltinFnIdAddWithOverflow:
@@ -8585,25 +8615,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 //    }
 //}
 //
-//
-//
-//static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeBinOpExpr);
-//
-//    AstNode *lhs_node = node->data.bin_op_expr.op1;
-//
-//    TypeTableEntry *op1_type;
-//
-//    LLVMValueRef target_ref = gen_lvalue(g, node, lhs_node, &op1_type);
-//
-//    TypeTableEntry *op2_type = get_expr_type(node->data.bin_op_expr.op2);
-//
-//    LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2);
-//
-//    gen_assign_raw(g, node, node->data.bin_op_expr.bin_op, target_ref, value, op1_type, op2_type);
-//    return nullptr;
-//}
-//
 //static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {
 //    assert(node->type == NodeTypeUnwrapErrorExpr);
 //
@@ -8894,71 +8905,3 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 //    gen_var_debug_decl(g, variable);
 //    return nullptr;
 //}
-//
-//static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) {
-//    assert(node->type == NodeTypeArrayAccessExpr);
-//
-//    LLVMValueRef ptr = gen_array_ptr(g, node);
-//    TypeTableEntry *child_type;
-//    TypeTableEntry *array_type = get_expr_type(node->data.array_access_expr.array_ref_expr);
-//    if (array_type->id == TypeTableEntryIdPointer) {
-//        child_type = array_type->data.pointer.child_type;
-//    } else if (array_type->id == TypeTableEntryIdStruct) {
-//        assert(array_type->data.structure.is_slice);
-//        TypeTableEntry *child_ptr_type = array_type->data.structure.fields[0].type_entry;
-//        assert(child_ptr_type->id == TypeTableEntryIdPointer);
-//        child_type = child_ptr_type->data.pointer.child_type;
-//    } else if (array_type->id == TypeTableEntryIdArray) {
-//        child_type = array_type->data.array.child_type;
-//    } else {
-//        zig_unreachable();
-//    }
-//
-//    if (is_lvalue || !ptr || handle_is_ptr(child_type)) {
-//        return ptr;
-//    } else {
-//        return LLVMBuildLoad(g->builder, ptr, "");
-//    }
-//}
-//
-//static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) {
-//    AstNode *init_expr = node->data.variable_declaration.expr;
-//    if (node->data.variable_declaration.is_const && init_expr) {
-//        TypeTableEntry *init_expr_type = get_expr_type(init_expr);
-//        if (init_expr_type->id == TypeTableEntryIdNumLitFloat ||
-//            init_expr_type->id == TypeTableEntryIdNumLitInt)
-//        {
-//            return nullptr;
-//        }
-//    }
-//
-//    LLVMValueRef init_val = nullptr;
-//    TypeTableEntry *init_val_type;
-//    return gen_var_decl_raw(g, node, &node->data.variable_declaration, false, &init_val, &init_val_type, false);
-//}
-//
-//static LLVMValueRef gen_fence(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeFnCallExpr);
-//
-//    AstNode *atomic_order_arg = node->data.fn_call_expr.params.at(0);
-//    ConstExprValue *atomic_order_val = &get_resolved_expr(atomic_order_arg)->const_val;
-//
-//    assert(atomic_order_val->ok);
-//
-//    LLVMAtomicOrdering atomic_order = to_LLVMAtomicOrdering((AtomicOrder)atomic_order_val->data.x_enum.tag);
-//
-//    LLVMBuildFence(g->builder, atomic_order, false, "");
-//    return nullptr;
-//}
-//
-//static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
-//    switch (atomic_order) {
-//        case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered;
-//        case AtomicOrderMonotonic: return LLVMAtomicOrderingMonotonic;
-//        case AtomicOrderAcquire: return LLVMAtomicOrderingAcquire;
-//        case AtomicOrderRelease: return LLVMAtomicOrderingRelease;
-//        case AtomicOrderAcqRel: return LLVMAtomicOrderingAcquireRelease;
-//        case AtomicOrderSeqCst: return LLVMAtomicOrderingSequentiallyConsistent;
-//    }
-//    zig_unreachable();
-//}
src/ir_print.cpp
@@ -719,6 +719,26 @@ static void ir_print_embed_file(IrPrint *irp, IrInstructionEmbedFile *instructio
     fprintf(irp->f, ")");
 }
 
+static void ir_print_cmpxchg(IrPrint *irp, IrInstructionCmpxchg *instruction) {
+    fprintf(irp->f, "@cmpxchg(");
+    ir_print_other_instruction(irp, instruction->ptr);
+    fprintf(irp->f, ", ");
+    ir_print_other_instruction(irp, instruction->cmp_value);
+    fprintf(irp->f, ", ");
+    ir_print_other_instruction(irp, instruction->new_value);
+    fprintf(irp->f, ", ");
+    ir_print_other_instruction(irp, instruction->success_order_value);
+    fprintf(irp->f, ", ");
+    ir_print_other_instruction(irp, instruction->failure_order_value);
+    fprintf(irp->f, ")");
+}
+
+static void ir_print_fence(IrPrint *irp, IrInstructionFence *instruction) {
+    fprintf(irp->f, "@fence(");
+    ir_print_other_instruction(irp, instruction->order_value);
+    fprintf(irp->f, ")");
+}
+
 static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
     ir_print_prefix(irp, instruction);
     switch (instruction->id) {
@@ -883,6 +903,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdEmbedFile:
             ir_print_embed_file(irp, (IrInstructionEmbedFile *)instruction);
             break;
+        case IrInstructionIdCmpxchg:
+            ir_print_cmpxchg(irp, (IrInstructionCmpxchg *)instruction);
+            break;
+        case IrInstructionIdFence:
+            ir_print_fence(irp, (IrInstructionFence *)instruction);
+            break;
     }
     fprintf(irp->f, "\n");
 }
test/self_hosted2.zig
@@ -273,6 +273,18 @@ fn testErrorName() {
 //    return result;
 //}
 
+fn cmpxchg() {
+    var x: i32 = 1234;
+    while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {}
+    assert(x == 5678);
+}
+
+fn fence() {
+    var x: i32 = 1234;
+    @fence(AtomicOrder.SeqCst);
+    x = 5678;
+}
+
 fn assert(ok: bool) {
     if (!ok)
         @unreachable();
@@ -300,6 +312,8 @@ fn runAllTests() {
     testMinValueAndMaxValue();
     testReturnStringFromFunction();
     testErrorName();
+    cmpxchg();
+    fence();
 }
 
 export nakedcc fn _start() -> unreachable {