| author | |
| committer | |
| log | 42aa1ea115eca3dcc704eddf020ce87271a41174 |
| tree | 3242b53425c599af929446e6776a2f6b3a0c6966 |
| parent | 87fd502fb68f8f488e6eba6b1f7d70902d6bfe5a |
14 files changed, 412 insertions(+), 36 deletions(-)
src/Air.zig+15| ... | @@ -321,6 +321,19 @@ pub const Inst = struct { | ... | @@ -321,6 +321,19 @@ pub const Inst = struct { |
| 321 | /// Uses the `ty_op` field. | 321 | /// Uses the `ty_op` field. |
| 322 | int_to_float, | 322 | int_to_float, |
| 323 | 323 | ||
| 324 | /// Given dest ptr, value, and len, set all elements at dest to value. | ||
| 325 | /// Result type is always void. | ||
| 326 | /// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the | ||
| 327 | /// value, `rhs` is the length. | ||
| 328 | /// The element type may be any type, not just u8. | ||
| 329 | memset, | ||
| 330 | /// Given dest ptr, src ptr, and len, copy len elements from src to dest. | ||
| 331 | /// Result type is always void. | ||
| 332 | /// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the | ||
| 333 | /// src ptr, `rhs` is the length. | ||
| 334 | /// The element type may be any type, not just u8. | ||
| 335 | memcpy, | ||
| 336 | |||
| 324 | /// Uses the `ty_pl` field with payload `Cmpxchg`. | 337 | /// Uses the `ty_pl` field with payload `Cmpxchg`. |
| 325 | cmpxchg_weak, | 338 | cmpxchg_weak, |
| 326 | /// Uses the `ty_pl` field with payload `Cmpxchg`. | 339 | /// Uses the `ty_pl` field with payload `Cmpxchg`. |
| ... | @@ -628,6 +641,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -628,6 +641,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 628 | .atomic_store_monotonic, | 641 | .atomic_store_monotonic, |
| 629 | .atomic_store_release, | 642 | .atomic_store_release, |
| 630 | .atomic_store_seq_cst, | 643 | .atomic_store_seq_cst, |
| 644 | .memset, | ||
| 645 | .memcpy, | ||
| 631 | => return Type.initTag(.void), | 646 | => return Type.initTag(.void), |
| 632 | 647 | ||
| 633 | .ptrtoint, | 648 | .ptrtoint, |
src/AstGen.zig+8-8| ... | @@ -2149,8 +2149,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner | ... | @@ -2149,8 +2149,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 2149 | .field_ptr_type, | 2149 | .field_ptr_type, |
| 2150 | .field_parent_ptr, | 2150 | .field_parent_ptr, |
| 2151 | .maximum, | 2151 | .maximum, |
| 2152 | .memcpy, | ||
| 2153 | .memset, | ||
| 2154 | .minimum, | 2152 | .minimum, |
| 2155 | .builtin_async_call, | 2153 | .builtin_async_call, |
| 2156 | .c_import, | 2154 | .c_import, |
| ... | @@ -2204,6 +2202,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner | ... | @@ -2204,6 +2202,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 2204 | .set_float_mode, | 2202 | .set_float_mode, |
| 2205 | .set_runtime_safety, | 2203 | .set_runtime_safety, |
| 2206 | .closure_capture, | 2204 | .closure_capture, |
| 2205 | .memcpy, | ||
| 2206 | .memset, | ||
| 2207 | => break :b true, | 2207 | => break :b true, |
| 2208 | } | 2208 | } |
| 2209 | } else switch (maybe_unused_result) { | 2209 | } else switch (maybe_unused_result) { |
| ... | @@ -7576,17 +7576,17 @@ fn builtinCall( | ... | @@ -7576,17 +7576,17 @@ fn builtinCall( |
| 7576 | }, | 7576 | }, |
| 7577 | .memcpy => { | 7577 | .memcpy => { |
| 7578 | const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{ | 7578 | const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{ |
| 7579 | .dest = try expr(gz, scope, .{ .ty = .manyptr_u8_type }, params[0]), | 7579 | .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]), |
| 7580 | .source = try expr(gz, scope, .{ .ty = .manyptr_const_u8_type }, params[1]), | 7580 | .source = try expr(gz, scope, .{ .coerced_ty = .manyptr_const_u8_type }, params[1]), |
| 7581 | .byte_count = try expr(gz, scope, .{ .ty = .usize_type }, params[2]), | 7581 | .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]), |
| 7582 | }); | 7582 | }); |
| 7583 | return rvalue(gz, rl, result, node); | 7583 | return rvalue(gz, rl, result, node); |
| 7584 | }, | 7584 | }, |
| 7585 | .memset => { | 7585 | .memset => { |
| 7586 | const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{ | 7586 | const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{ |
| 7587 | .dest = try expr(gz, scope, .{ .ty = .manyptr_u8_type }, params[0]), | 7587 | .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]), |
| 7588 | .byte = try expr(gz, scope, .{ .ty = .u8_type }, params[1]), | 7588 | .byte = try expr(gz, scope, .{ .coerced_ty = .u8_type }, params[1]), |
| 7589 | .byte_count = try expr(gz, scope, .{ .ty = .usize_type }, params[2]), | 7589 | .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]), |
| 7590 | }); | 7590 | }); |
| 7591 | return rvalue(gz, rl, result, node); | 7591 | return rvalue(gz, rl, result, node); |
| 7592 | }, | 7592 | }, |
src/Liveness.zig+5| ... | @@ -361,6 +361,11 @@ fn analyzeInst( | ... | @@ -361,6 +361,11 @@ fn analyzeInst( |
| 361 | const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data; | 361 | const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data; |
| 362 | return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.operand, .none }); | 362 | return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.operand, .none }); |
| 363 | }, | 363 | }, |
| 364 | .memset, .memcpy => { | ||
| 365 | const pl_op = inst_datas[inst].pl_op; | ||
| 366 | const extra = a.air.extraData(Air.Bin, pl_op.payload).data; | ||
| 367 | return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs }); | ||
| 368 | }, | ||
| 364 | .br => { | 369 | .br => { |
| 365 | const br = inst_datas[inst].br; | 370 | const br = inst_datas[inst].br; |
| 366 | return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none }); | 371 | return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none }); |
src/Sema.zig+119-7| ... | @@ -341,8 +341,6 @@ pub fn analyzeBody( | ... | @@ -341,8 +341,6 @@ pub fn analyzeBody( |
| 341 | .field_ptr_type => try sema.zirFieldPtrType(block, inst), | 341 | .field_ptr_type => try sema.zirFieldPtrType(block, inst), |
| 342 | .field_parent_ptr => try sema.zirFieldParentPtr(block, inst), | 342 | .field_parent_ptr => try sema.zirFieldParentPtr(block, inst), |
| 343 | .maximum => try sema.zirMaximum(block, inst), | 343 | .maximum => try sema.zirMaximum(block, inst), |
| 344 | .memcpy => try sema.zirMemcpy(block, inst), | ||
| 345 | .memset => try sema.zirMemset(block, inst), | ||
| 346 | .minimum => try sema.zirMinimum(block, inst), | 344 | .minimum => try sema.zirMinimum(block, inst), |
| 347 | .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst), | 345 | .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst), |
| 348 | .@"resume" => try sema.zirResume(block, inst), | 346 | .@"resume" => try sema.zirResume(block, inst), |
| ... | @@ -526,6 +524,16 @@ pub fn analyzeBody( | ... | @@ -526,6 +524,16 @@ pub fn analyzeBody( |
| 526 | i += 1; | 524 | i += 1; |
| 527 | continue; | 525 | continue; |
| 528 | }, | 526 | }, |
| 527 | .memcpy => { | ||
| 528 | try sema.zirMemcpy(block, inst); | ||
| 529 | i += 1; | ||
| 530 | continue; | ||
| 531 | }, | ||
| 532 | .memset => { | ||
| 533 | try sema.zirMemset(block, inst); | ||
| 534 | i += 1; | ||
| 535 | continue; | ||
| 536 | }, | ||
| 529 | 537 | ||
| 530 | // Special case instructions to handle comptime control flow. | 538 | // Special case instructions to handle comptime control flow. |
| 531 | .@"break" => { | 539 | .@"break" => { |
| ... | @@ -8422,16 +8430,119 @@ fn zirMaximum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr | ... | @@ -8422,16 +8430,119 @@ fn zirMaximum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr |
| 8422 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMaximum", .{}); | 8430 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMaximum", .{}); |
| 8423 | } | 8431 | } |
| 8424 | 8432 | ||
| 8425 | fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 8433 | fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { |
| 8426 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 8434 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 8435 | const extra = sema.code.extraData(Zir.Inst.Memcpy, inst_data.payload_index).data; | ||
| 8427 | const src = inst_data.src(); | 8436 | const src = inst_data.src(); |
| 8428 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemcpy", .{}); | 8437 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 8438 | const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | ||
| 8439 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | ||
| 8440 | const dest_ptr = sema.resolveInst(extra.dest); | ||
| 8441 | const dest_ptr_ty = sema.typeOf(dest_ptr); | ||
| 8442 | |||
| 8443 | if (dest_ptr_ty.zigTypeTag() != .Pointer) { | ||
| 8444 | return sema.mod.fail(&block.base, dest_src, "expected pointer, found '{}'", .{dest_ptr_ty}); | ||
| 8445 | } | ||
| 8446 | if (dest_ptr_ty.isConstPtr()) { | ||
| 8447 | return sema.mod.fail(&block.base, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty}); | ||
| 8448 | } | ||
| 8449 | |||
| 8450 | const uncasted_src_ptr = sema.resolveInst(extra.source); | ||
| 8451 | const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr); | ||
| 8452 | if (uncasted_src_ptr_ty.zigTypeTag() != .Pointer) { | ||
| 8453 | return sema.mod.fail(&block.base, src_src, "expected pointer, found '{}'", .{ | ||
| 8454 | uncasted_src_ptr_ty, | ||
| 8455 | }); | ||
| 8456 | } | ||
| 8457 | const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data; | ||
| 8458 | const wanted_src_ptr_ty = try Module.ptrType( | ||
| 8459 | sema.arena, | ||
| 8460 | dest_ptr_ty.elemType2(), | ||
| 8461 | null, | ||
| 8462 | src_ptr_info.@"align", | ||
| 8463 | src_ptr_info.@"addrspace", | ||
| 8464 | 0, | ||
| 8465 | 0, | ||
| 8466 | false, | ||
| 8467 | src_ptr_info.@"allowzero", | ||
| 8468 | src_ptr_info.@"volatile", | ||
| 8469 | .Many, | ||
| 8470 | ); | ||
| 8471 | const src_ptr = try sema.coerce(block, wanted_src_ptr_ty, uncasted_src_ptr, src_src); | ||
| 8472 | const len = try sema.coerce(block, Type.initTag(.usize), sema.resolveInst(extra.byte_count), len_src); | ||
| 8473 | |||
| 8474 | const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr); | ||
| 8475 | const maybe_src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr); | ||
| 8476 | const maybe_len_val = try sema.resolveDefinedValue(block, len_src, len); | ||
| 8477 | |||
| 8478 | const runtime_src = if (maybe_dest_ptr_val) |dest_ptr_val| rs: { | ||
| 8479 | if (maybe_src_ptr_val) |src_ptr_val| { | ||
| 8480 | if (maybe_len_val) |len_val| { | ||
| 8481 | _ = dest_ptr_val; | ||
| 8482 | _ = src_ptr_val; | ||
| 8483 | _ = len_val; | ||
| 8484 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemcpy at comptime", .{}); | ||
| 8485 | } else break :rs len_src; | ||
| 8486 | } else break :rs src_src; | ||
| 8487 | } else dest_src; | ||
| 8488 | |||
| 8489 | try sema.requireRuntimeBlock(block, runtime_src); | ||
| 8490 | _ = try block.addInst(.{ | ||
| 8491 | .tag = .memcpy, | ||
| 8492 | .data = .{ .pl_op = .{ | ||
| 8493 | .operand = dest_ptr, | ||
| 8494 | .payload = try sema.addExtra(Air.Bin{ | ||
| 8495 | .lhs = src_ptr, | ||
| 8496 | .rhs = len, | ||
| 8497 | }), | ||
| 8498 | } }, | ||
| 8499 | }); | ||
| 8429 | } | 8500 | } |
| 8430 | 8501 | ||
| 8431 | fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 8502 | fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { |
| 8432 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 8503 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 8504 | const extra = sema.code.extraData(Zir.Inst.Memset, inst_data.payload_index).data; | ||
| 8433 | const src = inst_data.src(); | 8505 | const src = inst_data.src(); |
| 8434 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset", .{}); | 8506 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 8507 | const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | ||
| 8508 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | ||
| 8509 | const dest_ptr = sema.resolveInst(extra.dest); | ||
| 8510 | const dest_ptr_ty = sema.typeOf(dest_ptr); | ||
| 8511 | if (dest_ptr_ty.zigTypeTag() != .Pointer) { | ||
| 8512 | return sema.mod.fail(&block.base, dest_src, "expected pointer, found '{}'", .{dest_ptr_ty}); | ||
| 8513 | } | ||
| 8514 | if (dest_ptr_ty.isConstPtr()) { | ||
| 8515 | return sema.mod.fail(&block.base, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty}); | ||
| 8516 | } | ||
| 8517 | const elem_ty = dest_ptr_ty.elemType2(); | ||
| 8518 | const value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.byte), value_src); | ||
| 8519 | const len = try sema.coerce(block, Type.initTag(.usize), sema.resolveInst(extra.byte_count), len_src); | ||
| 8520 | |||
| 8521 | const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr); | ||
| 8522 | const maybe_len_val = try sema.resolveDefinedValue(block, len_src, len); | ||
| 8523 | |||
| 8524 | const runtime_src = if (maybe_dest_ptr_val) |ptr_val| rs: { | ||
| 8525 | if (maybe_len_val) |len_val| { | ||
| 8526 | if (try sema.resolveMaybeUndefVal(block, value_src, value)) |val| { | ||
| 8527 | _ = ptr_val; | ||
| 8528 | _ = len_val; | ||
| 8529 | _ = val; | ||
| 8530 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset at comptime", .{}); | ||
| 8531 | } else break :rs value_src; | ||
| 8532 | } else break :rs len_src; | ||
| 8533 | } else dest_src; | ||
| 8534 | |||
| 8535 | try sema.requireRuntimeBlock(block, runtime_src); | ||
| 8536 | _ = try block.addInst(.{ | ||
| 8537 | .tag = .memset, | ||
| 8538 | .data = .{ .pl_op = .{ | ||
| 8539 | .operand = dest_ptr, | ||
| 8540 | .payload = try sema.addExtra(Air.Bin{ | ||
| 8541 | .lhs = value, | ||
| 8542 | .rhs = len, | ||
| 8543 | }), | ||
| 8544 | } }, | ||
| 8545 | }); | ||
| 8435 | } | 8546 | } |
| 8436 | 8547 | ||
| 8437 | fn zirMinimum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 8548 | fn zirMinimum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -10090,7 +10201,8 @@ fn coerceArrayPtrToMany( | ... | @@ -10090,7 +10201,8 @@ fn coerceArrayPtrToMany( |
| 10090 | // The comptime Value representation is compatible with both types. | 10201 | // The comptime Value representation is compatible with both types. |
| 10091 | return sema.addConstant(dest_type, val); | 10202 | return sema.addConstant(dest_type, val); |
| 10092 | } | 10203 | } |
| 10093 | return sema.mod.fail(&block.base, inst_src, "TODO implement coerceArrayPtrToMany runtime instruction", .{}); | 10204 | try sema.requireRuntimeBlock(block, inst_src); |
| 10205 | return sema.bitcast(block, dest_type, inst, inst_src); | ||
| 10094 | } | 10206 | } |
| 10095 | 10207 | ||
| 10096 | fn analyzeDeclVal( | 10208 | fn analyzeDeclVal( |
src/codegen.zig+12| ... | @@ -887,6 +887,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -887,6 +887,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 887 | .cmpxchg_weak => try self.airCmpxchg(inst), | 887 | .cmpxchg_weak => try self.airCmpxchg(inst), |
| 888 | .atomic_rmw => try self.airAtomicRmw(inst), | 888 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 889 | .atomic_load => try self.airAtomicLoad(inst), | 889 | .atomic_load => try self.airAtomicLoad(inst), |
| 890 | .memcpy => try self.airMemcpy(inst), | ||
| 891 | .memset => try self.airMemset(inst), | ||
| 890 | 892 | ||
| 891 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), | 893 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 892 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), | 894 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| ... | @@ -4883,6 +4885,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -4883,6 +4885,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4883 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); | 4885 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); |
| 4884 | } | 4886 | } |
| 4885 | 4887 | ||
| 4888 | fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | ||
| 4889 | _ = inst; | ||
| 4890 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); | ||
| 4891 | } | ||
| 4892 | |||
| 4893 | fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { | ||
| 4894 | _ = inst; | ||
| 4895 | return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); | ||
| 4896 | } | ||
| 4897 | |||
| 4886 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { | 4898 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 4887 | // First section of indexes correspond to a set number of constant values. | 4899 | // First section of indexes correspond to a set number of constant values. |
| 4888 | const ref_int = @enumToInt(inst); | 4900 | const ref_int = @enumToInt(inst); |
src/codegen/c.zig+45-1| ... | @@ -953,6 +953,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -953,6 +953,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 953 | .cmpxchg_strong => try airCmpxchg(f, inst, "strong"), | 953 | .cmpxchg_strong => try airCmpxchg(f, inst, "strong"), |
| 954 | .atomic_rmw => try airAtomicRmw(f, inst), | 954 | .atomic_rmw => try airAtomicRmw(f, inst), |
| 955 | .atomic_load => try airAtomicLoad(f, inst), | 955 | .atomic_load => try airAtomicLoad(f, inst), |
| 956 | .memset => try airMemset(f, inst), | ||
| 957 | .memcpy => try airMemcpy(f, inst), | ||
| 956 | 958 | ||
| 957 | .int_to_float, | 959 | .int_to_float, |
| 958 | .float_to_int, | 960 | .float_to_int, |
| ... | @@ -2005,8 +2007,12 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -2005,8 +2007,12 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2005 | 2007 | ||
| 2006 | fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { | 2008 | fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2007 | const atomic_load = f.air.instructions.items(.data)[inst].atomic_load; | 2009 | const atomic_load = f.air.instructions.items(.data)[inst].atomic_load; |
| 2008 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 2009 | const ptr = try f.resolveInst(atomic_load.ptr); | 2010 | const ptr = try f.resolveInst(atomic_load.ptr); |
| 2011 | const ptr_ty = f.air.typeOf(atomic_load.ptr); | ||
| 2012 | if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) | ||
| 2013 | return CValue.none; | ||
| 2014 | |||
| 2015 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 2010 | const local = try f.allocLocal(inst_ty, .Const); | 2016 | const local = try f.allocLocal(inst_ty, .Const); |
| 2011 | const writer = f.object.writer(); | 2017 | const writer = f.object.writer(); |
| 2012 | 2018 | ||
| ... | @@ -2036,6 +2042,44 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa | ... | @@ -2036,6 +2042,44 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa |
| 2036 | return local; | 2042 | return local; |
| 2037 | } | 2043 | } |
| 2038 | 2044 | ||
| 2045 | fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { | ||
| 2046 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | ||
| 2047 | const extra = f.air.extraData(Air.Bin, pl_op.payload).data; | ||
| 2048 | const dest_ptr = try f.resolveInst(pl_op.operand); | ||
| 2049 | const value = try f.resolveInst(extra.lhs); | ||
| 2050 | const len = try f.resolveInst(extra.rhs); | ||
| 2051 | const writer = f.object.writer(); | ||
| 2052 | |||
| 2053 | try writer.writeAll("memset("); | ||
| 2054 | try f.writeCValue(writer, dest_ptr); | ||
| 2055 | try writer.writeAll(", "); | ||
| 2056 | try f.writeCValue(writer, value); | ||
| 2057 | try writer.writeAll(", "); | ||
| 2058 | try f.writeCValue(writer, len); | ||
| 2059 | try writer.writeAll(");\n"); | ||
| 2060 | |||
| 2061 | return CValue.none; | ||
| 2062 | } | ||
| 2063 | |||
| 2064 | fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { | ||
| 2065 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | ||
| 2066 | const extra = f.air.extraData(Air.Bin, pl_op.payload).data; | ||
| 2067 | const dest_ptr = try f.resolveInst(pl_op.operand); | ||
| 2068 | const src_ptr = try f.resolveInst(extra.lhs); | ||
| 2069 | const len = try f.resolveInst(extra.rhs); | ||
| 2070 | const writer = f.object.writer(); | ||
| 2071 | |||
| 2072 | try writer.writeAll("memcpy("); | ||
| 2073 | try f.writeCValue(writer, dest_ptr); | ||
| 2074 | try writer.writeAll(", "); | ||
| 2075 | try f.writeCValue(writer, src_ptr); | ||
| 2076 | try writer.writeAll(", "); | ||
| 2077 | try f.writeCValue(writer, len); | ||
| 2078 | try writer.writeAll(");\n"); | ||
| 2079 | |||
| 2080 | return CValue.none; | ||
| 2081 | } | ||
| 2082 | |||
| 2039 | fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { | 2083 | fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { |
| 2040 | return switch (order) { | 2084 | return switch (order) { |
| 2041 | .Unordered => "memory_order_relaxed", | 2085 | .Unordered => "memory_order_relaxed", |
src/codegen/llvm.zig+53| ... | @@ -1279,6 +1279,8 @@ pub const FuncGen = struct { | ... | @@ -1279,6 +1279,8 @@ pub const FuncGen = struct { |
| 1279 | .fence => try self.airFence(inst), | 1279 | .fence => try self.airFence(inst), |
| 1280 | .atomic_rmw => try self.airAtomicRmw(inst), | 1280 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 1281 | .atomic_load => try self.airAtomicLoad(inst), | 1281 | .atomic_load => try self.airAtomicLoad(inst), |
| 1282 | .memset => try self.airMemset(inst), | ||
| 1283 | .memcpy => try self.airMemcpy(inst), | ||
| 1282 | 1284 | ||
| 1283 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), | 1285 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 1284 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), | 1286 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| ... | @@ -2426,6 +2428,8 @@ pub const FuncGen = struct { | ... | @@ -2426,6 +2428,8 @@ pub const FuncGen = struct { |
| 2426 | const atomic_load = self.air.instructions.items(.data)[inst].atomic_load; | 2428 | const atomic_load = self.air.instructions.items(.data)[inst].atomic_load; |
| 2427 | const ptr = try self.resolveInst(atomic_load.ptr); | 2429 | const ptr = try self.resolveInst(atomic_load.ptr); |
| 2428 | const ptr_ty = self.air.typeOf(atomic_load.ptr); | 2430 | const ptr_ty = self.air.typeOf(atomic_load.ptr); |
| 2431 | if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) | ||
| 2432 | return null; | ||
| 2429 | const ordering = toLlvmAtomicOrdering(atomic_load.order); | 2433 | const ordering = toLlvmAtomicOrdering(atomic_load.order); |
| 2430 | const operand_ty = ptr_ty.elemType(); | 2434 | const operand_ty = ptr_ty.elemType(); |
| 2431 | const opt_abi_ty = self.dg.getAtomicAbiType(operand_ty, false); | 2435 | const opt_abi_ty = self.dg.getAtomicAbiType(operand_ty, false); |
| ... | @@ -2468,6 +2472,55 @@ pub const FuncGen = struct { | ... | @@ -2468,6 +2472,55 @@ pub const FuncGen = struct { |
| 2468 | return null; | 2472 | return null; |
| 2469 | } | 2473 | } |
| 2470 | 2474 | ||
| 2475 | fn airMemset(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | ||
| 2476 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | ||
| 2477 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; | ||
| 2478 | const dest_ptr = try self.resolveInst(pl_op.operand); | ||
| 2479 | const ptr_ty = self.air.typeOf(pl_op.operand); | ||
| 2480 | const value = try self.resolveInst(extra.lhs); | ||
| 2481 | const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndef() else false; | ||
| 2482 | const len = try self.resolveInst(extra.rhs); | ||
| 2483 | const u8_llvm_ty = self.context.intType(8); | ||
| 2484 | const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0); | ||
| 2485 | const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, ""); | ||
| 2486 | const fill_char = if (val_is_undef) u8_llvm_ty.constInt(0xaa, .False) else value; | ||
| 2487 | const target = self.dg.module.getTarget(); | ||
| 2488 | const dest_ptr_align = ptr_ty.ptrAlignment(target); | ||
| 2489 | const memset = self.builder.buildMemSet(dest_ptr_u8, fill_char, len, dest_ptr_align); | ||
| 2490 | memset.setVolatile(llvm.Bool.fromBool(ptr_ty.isVolatilePtr())); | ||
| 2491 | |||
| 2492 | if (val_is_undef and self.dg.module.comp.bin_file.options.valgrind) { | ||
| 2493 | // TODO generate valgrind client request to mark byte range as undefined | ||
| 2494 | // see gen_valgrind_undef() in codegen.cpp | ||
| 2495 | } | ||
| 2496 | return null; | ||
| 2497 | } | ||
| 2498 | |||
| 2499 | fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | ||
| 2500 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | ||
| 2501 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; | ||
| 2502 | const dest_ptr = try self.resolveInst(pl_op.operand); | ||
| 2503 | const dest_ptr_ty = self.air.typeOf(pl_op.operand); | ||
| 2504 | const src_ptr = try self.resolveInst(extra.lhs); | ||
| 2505 | const src_ptr_ty = self.air.typeOf(extra.lhs); | ||
| 2506 | const len = try self.resolveInst(extra.rhs); | ||
| 2507 | const u8_llvm_ty = self.context.intType(8); | ||
| 2508 | const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0); | ||
| 2509 | const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, ""); | ||
| 2510 | const src_ptr_u8 = self.builder.buildBitCast(src_ptr, ptr_u8_llvm_ty, ""); | ||
| 2511 | const is_volatile = src_ptr_ty.isVolatilePtr() or dest_ptr_ty.isVolatilePtr(); | ||
| 2512 | const target = self.dg.module.getTarget(); | ||
| 2513 | const memcpy = self.builder.buildMemCpy( | ||
| 2514 | dest_ptr_u8, | ||
| 2515 | dest_ptr_ty.ptrAlignment(target), | ||
| 2516 | src_ptr_u8, | ||
| 2517 | src_ptr_ty.ptrAlignment(target), | ||
| 2518 | len, | ||
| 2519 | ); | ||
| 2520 | memcpy.setVolatile(llvm.Bool.fromBool(is_volatile)); | ||
| 2521 | return null; | ||
| 2522 | } | ||
| 2523 | |||
| 2471 | fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value { | 2524 | fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value { |
| 2472 | const id = llvm.lookupIntrinsicID(name.ptr, name.len); | 2525 | const id = llvm.lookupIntrinsicID(name.ptr, name.len); |
| 2473 | assert(id != 0); | 2526 | assert(id != 0); |
src/codegen/llvm/bindings.zig+19| ... | @@ -632,6 +632,25 @@ pub const Builder = opaque { | ... | @@ -632,6 +632,25 @@ pub const Builder = opaque { |
| 632 | DestTy: *const Type, | 632 | DestTy: *const Type, |
| 633 | Name: [*:0]const u8, | 633 | Name: [*:0]const u8, |
| 634 | ) *const Value; | 634 | ) *const Value; |
| 635 | |||
| 636 | pub const buildMemSet = LLVMBuildMemSet; | ||
| 637 | extern fn LLVMBuildMemSet( | ||
| 638 | B: *const Builder, | ||
| 639 | Ptr: *const Value, | ||
| 640 | Val: *const Value, | ||
| 641 | Len: *const Value, | ||
| 642 | Align: c_uint, | ||
| 643 | ) *const Value; | ||
| 644 | |||
| 645 | pub const buildMemCpy = LLVMBuildMemCpy; | ||
| 646 | extern fn LLVMBuildMemCpy( | ||
| 647 | B: *const Builder, | ||
| 648 | Dst: *const Value, | ||
| 649 | DstAlign: c_uint, | ||
| 650 | Src: *const Value, | ||
| 651 | SrcAlign: c_uint, | ||
| 652 | Size: *const Value, | ||
| 653 | ) *const Value; | ||
| 635 | }; | 654 | }; |
| 636 | 655 | ||
| 637 | pub const IntPredicate = enum(c_uint) { | 656 | pub const IntPredicate = enum(c_uint) { |
src/link/C/zig.h+1| ... | @@ -126,6 +126,7 @@ | ... | @@ -126,6 +126,7 @@ |
| 126 | #define int128_t __int128 | 126 | #define int128_t __int128 |
| 127 | #define uint128_t unsigned __int128 | 127 | #define uint128_t unsigned __int128 |
| 128 | ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); | 128 | ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); |
| 129 | ZIG_EXTERN_C void *memset (void *, int, size_t); | ||
| 129 | 130 | ||
| 130 | static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) { | 131 | static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) { |
| 131 | uint8_t thresh = max - rhs; | 132 | uint8_t thresh = max - rhs; |
src/print_air.zig+24| ... | @@ -202,6 +202,8 @@ const Writer = struct { | ... | @@ -202,6 +202,8 @@ const Writer = struct { |
| 202 | .atomic_store_release => try w.writeAtomicStore(s, inst, .Release), | 202 | .atomic_store_release => try w.writeAtomicStore(s, inst, .Release), |
| 203 | .atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .SeqCst), | 203 | .atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .SeqCst), |
| 204 | .atomic_rmw => try w.writeAtomicRmw(s, inst), | 204 | .atomic_rmw => try w.writeAtomicRmw(s, inst), |
| 205 | .memcpy => try w.writeMemcpy(s, inst), | ||
| 206 | .memset => try w.writeMemset(s, inst), | ||
| 205 | } | 207 | } |
| 206 | } | 208 | } |
| 207 | 209 | ||
| ... | @@ -322,6 +324,28 @@ const Writer = struct { | ... | @@ -322,6 +324,28 @@ const Writer = struct { |
| 322 | try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) }); | 324 | try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) }); |
| 323 | } | 325 | } |
| 324 | 326 | ||
| 327 | fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | ||
| 328 | const pl_op = w.air.instructions.items(.data)[inst].pl_op; | ||
| 329 | const extra = w.air.extraData(Air.Bin, pl_op.payload).data; | ||
| 330 | |||
| 331 | try w.writeOperand(s, inst, 0, pl_op.operand); | ||
| 332 | try s.writeAll(", "); | ||
| 333 | try w.writeOperand(s, inst, 1, extra.lhs); | ||
| 334 | try s.writeAll(", "); | ||
| 335 | try w.writeOperand(s, inst, 2, extra.rhs); | ||
| 336 | } | ||
| 337 | |||
| 338 | fn writeMemcpy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | ||
| 339 | const pl_op = w.air.instructions.items(.data)[inst].pl_op; | ||
| 340 | const extra = w.air.extraData(Air.Bin, pl_op.payload).data; | ||
| 341 | |||
| 342 | try w.writeOperand(s, inst, 0, pl_op.operand); | ||
| 343 | try s.writeAll(", "); | ||
| 344 | try w.writeOperand(s, inst, 1, extra.lhs); | ||
| 345 | try s.writeAll(", "); | ||
| 346 | try w.writeOperand(s, inst, 2, extra.rhs); | ||
| 347 | } | ||
| 348 | |||
| 325 | fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | 349 | fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 326 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; | 350 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; |
| 327 | const val = w.air.values[ty_pl.payload]; | 351 | const val = w.air.values[ty_pl.payload]; |
src/print_zir.zig+28-2| ... | @@ -210,8 +210,6 @@ const Writer = struct { | ... | @@ -210,8 +210,6 @@ const Writer = struct { |
| 210 | .mul_add, | 210 | .mul_add, |
| 211 | .builtin_call, | 211 | .builtin_call, |
| 212 | .field_parent_ptr, | 212 | .field_parent_ptr, |
| 213 | .memcpy, | ||
| 214 | .memset, | ||
| 215 | .builtin_async_call, | 213 | .builtin_async_call, |
| 216 | => try self.writePlNode(stream, inst), | 214 | => try self.writePlNode(stream, inst), |
| 217 | 215 | ||
| ... | @@ -222,6 +220,8 @@ const Writer = struct { | ... | @@ -222,6 +220,8 @@ const Writer = struct { |
| 222 | .cmpxchg_strong, .cmpxchg_weak => try self.writeCmpxchg(stream, inst), | 220 | .cmpxchg_strong, .cmpxchg_weak => try self.writeCmpxchg(stream, inst), |
| 223 | .atomic_store => try self.writeAtomicStore(stream, inst), | 221 | .atomic_store => try self.writeAtomicStore(stream, inst), |
| 224 | .atomic_rmw => try self.writeAtomicRmw(stream, inst), | 222 | .atomic_rmw => try self.writeAtomicRmw(stream, inst), |
| 223 | .memcpy => try self.writeMemcpy(stream, inst), | ||
| 224 | .memset => try self.writeMemset(stream, inst), | ||
| 225 | 225 | ||
| 226 | .struct_init_anon, | 226 | .struct_init_anon, |
| 227 | .struct_init_anon_ref, | 227 | .struct_init_anon_ref, |
| ... | @@ -692,6 +692,32 @@ const Writer = struct { | ... | @@ -692,6 +692,32 @@ const Writer = struct { |
| 692 | try self.writeSrc(stream, inst_data.src()); | 692 | try self.writeSrc(stream, inst_data.src()); |
| 693 | } | 693 | } |
| 694 | 694 | ||
| 695 | fn writeMemcpy(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | ||
| 696 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | ||
| 697 | const extra = self.code.extraData(Zir.Inst.Memcpy, inst_data.payload_index).data; | ||
| 698 | |||
| 699 | try self.writeInstRef(stream, extra.dest); | ||
| 700 | try stream.writeAll(", "); | ||
| 701 | try self.writeInstRef(stream, extra.source); | ||
| 702 | try stream.writeAll(", "); | ||
| 703 | try self.writeInstRef(stream, extra.byte_count); | ||
| 704 | try stream.writeAll(") "); | ||
| 705 | try self.writeSrc(stream, inst_data.src()); | ||
| 706 | } | ||
| 707 | |||
| 708 | fn writeMemset(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | ||
| 709 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | ||
| 710 | const extra = self.code.extraData(Zir.Inst.Memset, inst_data.payload_index).data; | ||
| 711 | |||
| 712 | try self.writeInstRef(stream, extra.dest); | ||
| 713 | try stream.writeAll(", "); | ||
| 714 | try self.writeInstRef(stream, extra.byte); | ||
| 715 | try stream.writeAll(", "); | ||
| 716 | try self.writeInstRef(stream, extra.byte_count); | ||
| 717 | try stream.writeAll(") "); | ||
| 718 | try self.writeSrc(stream, inst_data.src()); | ||
| 719 | } | ||
| 720 | |||
| 695 | fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | 721 | fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
| 696 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | 722 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 697 | const extra = self.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); | 723 | const extra = self.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); |
src/type.zig+65-8| ... | @@ -2391,12 +2391,11 @@ pub const Type = extern union { | ... | @@ -2391,12 +2391,11 @@ pub const Type = extern union { |
| 2391 | }; | 2391 | }; |
| 2392 | } | 2392 | } |
| 2393 | 2393 | ||
| 2394 | /// Asserts the type is a pointer or array type. | 2394 | pub fn childType(ty: Type) Type { |
| 2395 | pub fn elemType(self: Type) Type { | 2395 | return switch (ty.tag()) { |
| 2396 | return switch (self.tag()) { | 2396 | .vector => ty.castTag(.vector).?.data.elem_type, |
| 2397 | .vector => self.castTag(.vector).?.data.elem_type, | 2397 | .array => ty.castTag(.array).?.data.elem_type, |
| 2398 | .array => self.castTag(.array).?.data.elem_type, | 2398 | .array_sentinel => ty.castTag(.array_sentinel).?.data.elem_type, |
| 2399 | .array_sentinel => self.castTag(.array_sentinel).?.data.elem_type, | ||
| 2400 | .single_const_pointer, | 2399 | .single_const_pointer, |
| 2401 | .single_mut_pointer, | 2400 | .single_mut_pointer, |
| 2402 | .many_const_pointer, | 2401 | .many_const_pointer, |
| ... | @@ -2405,7 +2404,48 @@ pub const Type = extern union { | ... | @@ -2405,7 +2404,48 @@ pub const Type = extern union { |
| 2405 | .c_mut_pointer, | 2404 | .c_mut_pointer, |
| 2406 | .const_slice, | 2405 | .const_slice, |
| 2407 | .mut_slice, | 2406 | .mut_slice, |
| 2408 | => self.castPointer().?.data, | 2407 | => ty.castPointer().?.data, |
| 2408 | |||
| 2409 | .array_u8, | ||
| 2410 | .array_u8_sentinel_0, | ||
| 2411 | .const_slice_u8, | ||
| 2412 | .manyptr_u8, | ||
| 2413 | .manyptr_const_u8, | ||
| 2414 | => Type.initTag(.u8), | ||
| 2415 | |||
| 2416 | .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), | ||
| 2417 | .pointer => ty.castTag(.pointer).?.data.pointee_type, | ||
| 2418 | |||
| 2419 | else => unreachable, | ||
| 2420 | }; | ||
| 2421 | } | ||
| 2422 | |||
| 2423 | /// Asserts the type is a pointer or array type. | ||
| 2424 | /// TODO this is deprecated in favor of `childType`. | ||
| 2425 | pub const elemType = childType; | ||
| 2426 | |||
| 2427 | /// For *[N]T, returns T. | ||
| 2428 | /// For ?*T, returns T. | ||
| 2429 | /// For ?*[N]T, returns T. | ||
| 2430 | /// For ?[*]T, returns T. | ||
| 2431 | /// For *T, returns T. | ||
| 2432 | /// For [*]T, returns T. | ||
| 2433 | pub fn elemType2(ty: Type) Type { | ||
| 2434 | return switch (ty.tag()) { | ||
| 2435 | .vector => ty.castTag(.vector).?.data.elem_type, | ||
| 2436 | .array => ty.castTag(.array).?.data.elem_type, | ||
| 2437 | .array_sentinel => ty.castTag(.array_sentinel).?.data.elem_type, | ||
| 2438 | .many_const_pointer, | ||
| 2439 | .many_mut_pointer, | ||
| 2440 | .c_const_pointer, | ||
| 2441 | .c_mut_pointer, | ||
| 2442 | .const_slice, | ||
| 2443 | .mut_slice, | ||
| 2444 | => ty.castPointer().?.data, | ||
| 2445 | |||
| 2446 | .single_const_pointer, | ||
| 2447 | .single_mut_pointer, | ||
| 2448 | => ty.castPointer().?.data.shallowElemType(), | ||
| 2409 | 2449 | ||
| 2410 | .array_u8, | 2450 | .array_u8, |
| 2411 | .array_u8_sentinel_0, | 2451 | .array_u8_sentinel_0, |
| ... | @@ -2415,12 +2455,29 @@ pub const Type = extern union { | ... | @@ -2415,12 +2455,29 @@ pub const Type = extern union { |
| 2415 | => Type.initTag(.u8), | 2455 | => Type.initTag(.u8), |
| 2416 | 2456 | ||
| 2417 | .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), | 2457 | .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), |
| 2418 | .pointer => self.castTag(.pointer).?.data.pointee_type, | 2458 | .pointer => { |
| 2459 | const info = ty.castTag(.pointer).?.data; | ||
| 2460 | const child_ty = info.pointee_type; | ||
| 2461 | if (info.size == .One) { | ||
| 2462 | return child_ty.shallowElemType(); | ||
| 2463 | } else { | ||
| 2464 | return child_ty; | ||
| 2465 | } | ||
| 2466 | }, | ||
| 2467 | |||
| 2468 | // TODO handle optionals | ||
| 2419 | 2469 | ||
| 2420 | else => unreachable, | 2470 | else => unreachable, |
| 2421 | }; | 2471 | }; |
| 2422 | } | 2472 | } |
| 2423 | 2473 | ||
| 2474 | fn shallowElemType(child_ty: Type) Type { | ||
| 2475 | return switch (child_ty.zigTypeTag()) { | ||
| 2476 | .Array, .Vector => child_ty.childType(), | ||
| 2477 | else => child_ty, | ||
| 2478 | }; | ||
| 2479 | } | ||
| 2480 | |||
| 2424 | /// Asserts that the type is an optional. | 2481 | /// Asserts that the type is an optional. |
| 2425 | /// Resulting `Type` will have inner memory referencing `buf`. | 2482 | /// Resulting `Type` will have inner memory referencing `buf`. |
| 2426 | pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type { | 2483 | pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type { |
test/behavior/basic.zig+18| ... | @@ -170,3 +170,21 @@ test "string concatenation" { | ... | @@ -170,3 +170,21 @@ test "string concatenation" { |
| 170 | test "array mult operator" { | 170 | test "array mult operator" { |
| 171 | try expect(mem.eql(u8, "ab" ** 5, "ababababab")); | 171 | try expect(mem.eql(u8, "ab" ** 5, "ababababab")); |
| 172 | } | 172 | } |
| 173 | |||
| 174 | test "memcpy and memset intrinsics" { | ||
| 175 | try testMemcpyMemset(); | ||
| 176 | // TODO add comptime test coverage | ||
| 177 | //comptime try testMemcpyMemset(); | ||
| 178 | } | ||
| 179 | |||
| 180 | fn testMemcpyMemset() !void { | ||
| 181 | var foo: [20]u8 = undefined; | ||
| 182 | var bar: [20]u8 = undefined; | ||
| 183 | |||
| 184 | @memset(&foo, 'A', foo.len); | ||
| 185 | @memcpy(&bar, &foo, bar.len); | ||
| 186 | |||
| 187 | try expect(bar[0] == 'A'); | ||
| 188 | try expect(bar[11] == 'A'); | ||
| 189 | try expect(bar[19] == 'A'); | ||
| 190 | } |
test/behavior/misc.zig-10| ... | @@ -5,16 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings; | ... | @@ -5,16 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings; |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
| 6 | const builtin = @import("builtin"); | 6 | const builtin = @import("builtin"); |
| 7 | 7 | ||
| 8 | test "memcpy and memset intrinsics" { | ||
| 9 | var foo: [20]u8 = undefined; | ||
| 10 | var bar: [20]u8 = undefined; | ||
| 11 | |||
| 12 | @memset(&foo, 'A', foo.len); | ||
| 13 | @memcpy(&bar, &foo, bar.len); | ||
| 14 | |||
| 15 | if (bar[11] != 'A') unreachable; | ||
| 16 | } | ||
| 17 | |||
| 18 | test "slicing" { | 8 | test "slicing" { |
| 19 | var array: [20]i32 = undefined; | 9 | var array: [20]i32 = undefined; |
| 20 | 10 |