Skip to content

Commit 312d158

Browse files
authored
Factor out CopyOnWriteBlock. (#3785)
Generalize it to also support type blocks.
1 parent 2507c55 commit 312d158

File tree

3 files changed

+83
-43
lines changed

3 files changed

+83
-43
lines changed

toolchain/check/convert.cpp

Lines changed: 13 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "common/check.h"
1111
#include "llvm/ADT/STLExtras.h"
1212
#include "toolchain/check/context.h"
13+
#include "toolchain/sem_ir/copy_on_write_block.h"
1314
#include "toolchain/sem_ir/file.h"
1415
#include "toolchain/sem_ir/inst.h"
1516

@@ -194,47 +195,6 @@ static auto ConvertAggregateElement(
194195
return Convert(context, node_id, src_elem_id, target);
195196
}
196197

197-
namespace {
198-
// A handle to a new block that may be modified, with copy-on-write semantics.
199-
//
200-
// The constructor is given the ID of an existing block that provides the
201-
// initial contents of the new block. The new block is lazily allocated; if no
202-
// modifications have been made, the `id()` function will return the original
203-
// block ID.
204-
//
205-
// This is intended to avoid an unnecessary block allocation in the case where
206-
// the new block ends up being exactly the same as the original block.
207-
class CopyOnWriteBlock {
208-
public:
209-
// Constructs the block. If `source_id` is valid, it is used as the initial
210-
// value of the block. Otherwise, uninitialized storage for `size` elements
211-
// is allocated.
212-
CopyOnWriteBlock(SemIR::File& file, SemIR::InstBlockId source_id, size_t size)
213-
: file_(file), source_id_(source_id) {
214-
if (!source_id_.is_valid()) {
215-
id_ = file_.inst_blocks().AddUninitialized(size);
216-
}
217-
}
218-
219-
auto id() const -> SemIR::InstBlockId { return id_; }
220-
221-
auto Set(int i, SemIR::InstId value) -> void {
222-
if (source_id_.is_valid() && file_.inst_blocks().Get(id_)[i] == value) {
223-
return;
224-
}
225-
if (id_ == source_id_) {
226-
id_ = file_.inst_blocks().Add(file_.inst_blocks().Get(source_id_));
227-
}
228-
file_.inst_blocks().Get(id_)[i] = value;
229-
}
230-
231-
private:
232-
SemIR::File& file_;
233-
SemIR::InstBlockId source_id_;
234-
SemIR::InstBlockId id_ = source_id_;
235-
};
236-
} // namespace
237-
238198
// Performs a conversion from a tuple to an array type. This function only
239199
// converts the type, and does not perform a final conversion to the requested
240200
// expression category.
@@ -370,7 +330,12 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
370330
// Initialize each element of the destination from the corresponding element
371331
// of the source.
372332
// TODO: Annotate diagnostics coming from here with the element index.
373-
CopyOnWriteBlock new_block(sem_ir, literal_elems_id, src_elem_types.size());
333+
auto new_block =
334+
literal_elems_id.is_valid()
335+
? SemIR::CopyOnWriteInstBlock(sem_ir, literal_elems_id)
336+
: SemIR::CopyOnWriteInstBlock(
337+
sem_ir, SemIR::CopyOnWriteInstBlock::UninitializedBlock{
338+
src_elem_types.size()});
374339
for (auto [i, src_type_id, dest_type_id] :
375340
llvm::enumerate(src_elem_types, dest_elem_types)) {
376341
// TODO: This call recurses back into conversion. Switch to an iterative
@@ -463,7 +428,12 @@ static auto ConvertStructToStructOrClass(Context& context,
463428
// Initialize each element of the destination from the corresponding element
464429
// of the source.
465430
// TODO: Annotate diagnostics coming from here with the element index.
466-
CopyOnWriteBlock new_block(sem_ir, literal_elems_id, src_elem_fields.size());
431+
auto new_block =
432+
literal_elems_id.is_valid()
433+
? SemIR::CopyOnWriteInstBlock(sem_ir, literal_elems_id)
434+
: SemIR::CopyOnWriteInstBlock(
435+
sem_ir, SemIR::CopyOnWriteInstBlock::UninitializedBlock{
436+
src_elem_fields.size()});
467437
for (auto [i, dest_field_id] : llvm::enumerate(dest_elem_fields)) {
468438
auto dest_field =
469439
sem_ir.insts().GetAs<SemIR::StructTypeField>(dest_field_id);

toolchain/sem_ir/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ cc_library(
8282
hdrs = [
8383
"class.h",
8484
"constant.h",
85+
"copy_on_write_block.h",
8586
"file.h",
8687
"function.h",
8788
"impl.h",
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#ifndef CARBON_TOOLCHAIN_SEM_IR_COPY_ON_WRITE_BLOCK_H_
6+
#define CARBON_TOOLCHAIN_SEM_IR_COPY_ON_WRITE_BLOCK_H_
7+
8+
#include "toolchain/sem_ir/file.h"
9+
#include "toolchain/sem_ir/ids.h"
10+
11+
namespace Carbon::SemIR {
12+
13+
// A handle to a new block that may be modified, with copy-on-write semantics.
14+
//
15+
// The constructor is given the ID of an existing block that provides the
16+
// initial contents of the new block. The new block is lazily allocated; if no
17+
// modifications have been made, the `id()` function will return the original
18+
// block ID.
19+
//
20+
// This is intended to avoid an unnecessary block allocation in the case where
21+
// the new block ends up being exactly the same as the original block.
22+
template <typename BlockIdType, auto (SemIR::File::*ValueStore)()>
23+
class CopyOnWriteBlock {
24+
public:
25+
struct UninitializedBlock {
26+
size_t size;
27+
};
28+
29+
// Constructs the block. `source_id` is used as the initial value of the
30+
// block.
31+
CopyOnWriteBlock(SemIR::File& file, BlockIdType source_id)
32+
: file_(file), source_id_(source_id) {}
33+
34+
// Constructs the block, treating the original block as an uninitialized block
35+
// with `size` elements.
36+
CopyOnWriteBlock(SemIR::File& file, UninitializedBlock uninit)
37+
: file_(file),
38+
source_id_(BlockIdType::Invalid),
39+
id_(file_.inst_blocks().AddUninitialized(uninit.size)) {}
40+
41+
// Gets a block ID containing the resulting elements. Note that further
42+
// modifications may or may not allocate a new ID, so this should only be
43+
// called once all modifications have been performed.
44+
auto id() const -> BlockIdType { return id_; }
45+
46+
// Sets the element at index `i` within the block. Lazily allocates a new
47+
// block when the value changes for the first time.
48+
auto Set(int i, typename BlockIdType::ElementType value) -> void {
49+
if (source_id_.is_valid() && (file_.*ValueStore)().Get(id_)[i] == value) {
50+
return;
51+
}
52+
if (id_ == source_id_) {
53+
id_ = (file_.*ValueStore)().Add((file_.*ValueStore)().Get(source_id_));
54+
}
55+
(file_.*ValueStore)().Get(id_)[i] = value;
56+
}
57+
58+
private:
59+
SemIR::File& file_;
60+
BlockIdType source_id_;
61+
BlockIdType id_ = source_id_;
62+
};
63+
64+
using CopyOnWriteInstBlock = CopyOnWriteBlock<InstBlockId, &File::inst_blocks>;
65+
using CopyOnWriteTypeBlock = CopyOnWriteBlock<TypeBlockId, &File::type_blocks>;
66+
67+
} // namespace Carbon::SemIR
68+
69+
#endif // CARBON_TOOLCHAIN_SEM_IR_COPY_ON_WRITE_BLOCK_H_

0 commit comments

Comments
 (0)