copy_on_write_block.h 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  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. #ifndef CARBON_TOOLCHAIN_SEM_IR_COPY_ON_WRITE_BLOCK_H_
  5. #define CARBON_TOOLCHAIN_SEM_IR_COPY_ON_WRITE_BLOCK_H_
  6. #include "llvm/ADT/STLExtras.h"
  7. #include "toolchain/sem_ir/file.h"
  8. #include "toolchain/sem_ir/ids.h"
  9. namespace Carbon::SemIR {
  10. // A handle to a new block that may be modified, with copy-on-write semantics.
  11. //
  12. // The constructor is given the ID of an existing block that provides the
  13. // initial contents of the new block. The new block is lazily allocated; if no
  14. // modifications have been made, the `id()` function will return the original
  15. // block ID.
  16. //
  17. // This is intended to avoid an unnecessary block allocation in the case where
  18. // the new block ends up being exactly the same as the original block.
  19. template <auto (File::*ValueStore)()>
  20. class CopyOnWriteBlock {
  21. public:
  22. using BlockType = std::remove_cvref_t<
  23. typename llvm::function_traits<decltype(ValueStore)>::result_t>;
  24. struct UninitializedBlock {
  25. size_t size;
  26. };
  27. // Constructs the block. `source_id` is used as the initial value of the
  28. // block. `file` must not be null.
  29. explicit CopyOnWriteBlock(File* file, BlockType::IdType source_id)
  30. : file_(file), source_id_(source_id) {}
  31. // Constructs the block, treating the original block as an uninitialized block
  32. // with `size` elements. `file` must not be null.
  33. explicit CopyOnWriteBlock(File* file, UninitializedBlock uninit)
  34. : file_(file),
  35. source_id_(BlockType::IdType::None),
  36. id_((file_->*ValueStore)().AddUninitialized(uninit.size)) {}
  37. // Gets a block ID containing the resulting elements. Note that further
  38. // modifications may or may not allocate a new ID, so this should only be
  39. // called once all modifications have been performed.
  40. auto id() const -> BlockType::IdType { return id_; }
  41. // Gets a canonical block ID containing the resulting elements. This assumes
  42. // the original block ID, if specified, was also canonical.
  43. auto GetCanonical() const -> BlockType::IdType {
  44. return id_ == source_id_ ? id_ : (file_->*ValueStore)().MakeCanonical(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, BlockType::ElementType value) -> void {
  49. if (source_id_.has_value() && (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)().GetMutable(id_)[i] = value;
  56. }
  57. private:
  58. File* file_;
  59. BlockType::IdType source_id_;
  60. BlockType::IdType id_ = source_id_;
  61. };
  62. using CopyOnWriteInstBlock = CopyOnWriteBlock<&File::inst_blocks>;
  63. using CopyOnWriteStructTypeFieldsBlock =
  64. CopyOnWriteBlock<&File::struct_type_fields>;
  65. } // namespace Carbon::SemIR
  66. #endif // CARBON_TOOLCHAIN_SEM_IR_COPY_ON_WRITE_BLOCK_H_