summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-02-06 02:38:22 -0300
committerGravatar ameerj2021-07-22 21:51:21 -0400
commitda8096e6e35af250dcc56a1af76b8a211df63a90 (patch)
tree5bac3a389afddd1ba23a9fb2ea410c077c28f3b8
parentshader: Add pools and rename files (diff)
downloadyuzu-da8096e6e35af250dcc56a1af76b8a211df63a90.tar.gz
yuzu-da8096e6e35af250dcc56a1af76b8a211df63a90.tar.xz
yuzu-da8096e6e35af250dcc56a1af76b8a211df63a90.zip
shader: Properly store phi on Inst
-rw-r--r--src/shader_recompiler/frontend/ir/basic_block.cpp33
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.cpp102
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.h37
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc2
-rw-r--r--src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp22
-rw-r--r--src/shader_recompiler/object_pool.h11
6 files changed, 132 insertions, 75 deletions
diff --git a/src/shader_recompiler/frontend/ir/basic_block.cpp b/src/shader_recompiler/frontend/ir/basic_block.cpp
index 1a5d82135..50c6a83cd 100644
--- a/src/shader_recompiler/frontend/ir/basic_block.cpp
+++ b/src/shader_recompiler/frontend/ir/basic_block.cpp
@@ -129,26 +129,21 @@ std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>&
129 } else { 129 } else {
130 ret += fmt::format(" {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces 130 ret += fmt::format(" {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces
131 } 131 }
132 if (op == Opcode::Phi) { 132 const size_t arg_count{NumArgsOf(op)};
133 size_t val_index{0}; 133 for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) {
134 for (const auto& [phi_block, phi_val] : inst.PhiOperands()) { 134 const Value arg{inst.Arg(arg_index)};
135 ret += val_index != 0 ? ", " : " "; 135 const std::string arg_str{ArgToIndex(block_to_index, inst_to_index, arg)};
136 ret += fmt::format("[ {}, {} ]", ArgToIndex(block_to_index, inst_to_index, phi_val), 136 ret += arg_index != 0 ? ", " : " ";
137 BlockToIndex(block_to_index, phi_block)); 137 if (op == Opcode::Phi) {
138 ++val_index; 138 ret += fmt::format("[ {}, {} ]", arg_index,
139 BlockToIndex(block_to_index, inst.PhiBlock(arg_index)));
140 } else {
141 ret += arg_str;
139 } 142 }
140 } else { 143 const Type actual_type{arg.Type()};
141 const size_t arg_count{NumArgsOf(op)}; 144 const Type expected_type{ArgTypeOf(op, arg_index)};
142 for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) { 145 if (!AreTypesCompatible(actual_type, expected_type)) {
143 const Value arg{inst.Arg(arg_index)}; 146 ret += fmt::format("<type error: {} != {}>", actual_type, expected_type);
144 ret += arg_index != 0 ? ", " : " ";
145 ret += ArgToIndex(block_to_index, inst_to_index, arg);
146
147 const Type actual_type{arg.Type()};
148 const Type expected_type{ArgTypeOf(op, arg_index)};
149 if (!AreTypesCompatible(actual_type, expected_type)) {
150 ret += fmt::format("<type error: {} != {}>", actual_type, expected_type);
151 }
152 } 147 }
153 } 148 }
154 if (TypeOf(op) != Type::Void) { 149 if (TypeOf(op) != Type::Void) {
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp
index de953838c..e7ca92039 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.cpp
+++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory>
6 7
7#include "shader_recompiler/exception.h" 8#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/ir/microinstruction.h" 9#include "shader_recompiler/frontend/ir/microinstruction.h"
@@ -30,6 +31,22 @@ static void RemovePseudoInstruction(IR::Inst*& inst, IR::Opcode expected_opcode)
30 inst = nullptr; 31 inst = nullptr;
31} 32}
32 33
34Inst::Inst(IR::Opcode op_, u64 flags_) noexcept : op{op_}, flags{flags_} {
35 if (op == Opcode::Phi) {
36 std::construct_at(&phi_args);
37 } else {
38 std::construct_at(&args);
39 }
40}
41
42Inst::~Inst() {
43 if (op == Opcode::Phi) {
44 std::destroy_at(&phi_args);
45 } else {
46 std::destroy_at(&args);
47 }
48}
49
33bool Inst::MayHaveSideEffects() const noexcept { 50bool Inst::MayHaveSideEffects() const noexcept {
34 switch (op) { 51 switch (op) {
35 case Opcode::Branch: 52 case Opcode::Branch:
@@ -71,7 +88,10 @@ bool Inst::IsPseudoInstruction() const noexcept {
71 } 88 }
72} 89}
73 90
74bool Inst::AreAllArgsImmediates() const noexcept { 91bool Inst::AreAllArgsImmediates() const {
92 if (op == Opcode::Phi) {
93 throw LogicError("Testing for all arguments are immediates on phi instruction");
94 }
75 return std::all_of(args.begin(), args.begin() + NumArgs(), 95 return std::all_of(args.begin(), args.begin() + NumArgs(),
76 [](const IR::Value& value) { return value.IsImmediate(); }); 96 [](const IR::Value& value) { return value.IsImmediate(); });
77} 97}
@@ -101,7 +121,7 @@ Inst* Inst::GetAssociatedPseudoOperation(IR::Opcode opcode) {
101} 121}
102 122
103size_t Inst::NumArgs() const { 123size_t Inst::NumArgs() const {
104 return NumArgsOf(op); 124 return op == Opcode::Phi ? phi_args.size() : NumArgsOf(op);
105} 125}
106 126
107IR::Type Inst::Type() const { 127IR::Type Inst::Type() const {
@@ -109,13 +129,23 @@ IR::Type Inst::Type() const {
109} 129}
110 130
111Value Inst::Arg(size_t index) const { 131Value Inst::Arg(size_t index) const {
112 if (index >= NumArgsOf(op)) { 132 if (op == Opcode::Phi) {
113 throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op); 133 if (index >= phi_args.size()) {
134 throw InvalidArgument("Out of bounds argument index {} in phi instruction", index);
135 }
136 return phi_args[index].second;
137 } else {
138 if (index >= NumArgsOf(op)) {
139 throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op);
140 }
141 return args[index];
114 } 142 }
115 return args[index];
116} 143}
117 144
118void Inst::SetArg(size_t index, Value value) { 145void Inst::SetArg(size_t index, Value value) {
146 if (op == Opcode::Phi) {
147 throw LogicError("Setting argument on a phi instruction");
148 }
119 if (index >= NumArgsOf(op)) { 149 if (index >= NumArgsOf(op)) {
120 throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op); 150 throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op);
121 } 151 }
@@ -128,15 +158,21 @@ void Inst::SetArg(size_t index, Value value) {
128 args[index] = value; 158 args[index] = value;
129} 159}
130 160
131std::span<const std::pair<Block*, Value>> Inst::PhiOperands() const noexcept { 161Block* Inst::PhiBlock(size_t index) const {
132 return phi_operands; 162 if (op != Opcode::Phi) {
163 throw LogicError("{} is not a Phi instruction", op);
164 }
165 if (index >= phi_args.size()) {
166 throw InvalidArgument("Out of bounds argument index {} in phi instruction");
167 }
168 return phi_args[index].first;
133} 169}
134 170
135void Inst::AddPhiOperand(Block* predecessor, const Value& value) { 171void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
136 if (!value.IsImmediate()) { 172 if (!value.IsImmediate()) {
137 Use(value); 173 Use(value);
138 } 174 }
139 phi_operands.emplace_back(predecessor, value); 175 phi_args.emplace_back(predecessor, value);
140} 176}
141 177
142void Inst::Invalidate() { 178void Inst::Invalidate() {
@@ -145,18 +181,22 @@ void Inst::Invalidate() {
145} 181}
146 182
147void Inst::ClearArgs() { 183void Inst::ClearArgs() {
148 for (auto& value : args) { 184 if (op == Opcode::Phi) {
149 if (!value.IsImmediate()) { 185 for (auto& pair : phi_args) {
150 UndoUse(value); 186 IR::Value& value{pair.second};
187 if (!value.IsImmediate()) {
188 UndoUse(value);
189 }
151 } 190 }
152 value = {}; 191 phi_args.clear();
153 } 192 } else {
154 for (auto& [phi_block, phi_op] : phi_operands) { 193 for (auto& value : args) {
155 if (!phi_op.IsImmediate()) { 194 if (!value.IsImmediate()) {
156 UndoUse(phi_op); 195 UndoUse(value);
196 }
197 value = {};
157 } 198 }
158 } 199 }
159 phi_operands.clear();
160} 200}
161 201
162void Inst::ReplaceUsesWith(Value replacement) { 202void Inst::ReplaceUsesWith(Value replacement) {
@@ -167,24 +207,29 @@ void Inst::ReplaceUsesWith(Value replacement) {
167 if (!replacement.IsImmediate()) { 207 if (!replacement.IsImmediate()) {
168 Use(replacement); 208 Use(replacement);
169 } 209 }
170 args[0] = replacement; 210 if (op == Opcode::Phi) {
211 phi_args[0].second = replacement;
212 } else {
213 args[0] = replacement;
214 }
171} 215}
172 216
173void Inst::Use(const Value& value) { 217void Inst::Use(const Value& value) {
174 ++value.Inst()->use_count; 218 Inst* const inst{value.Inst()};
219 ++inst->use_count;
175 220
176 switch (op) { 221 switch (op) {
177 case Opcode::GetZeroFromOp: 222 case Opcode::GetZeroFromOp:
178 SetPseudoInstruction(value.Inst()->zero_inst, this); 223 SetPseudoInstruction(inst->zero_inst, this);
179 break; 224 break;
180 case Opcode::GetSignFromOp: 225 case Opcode::GetSignFromOp:
181 SetPseudoInstruction(value.Inst()->sign_inst, this); 226 SetPseudoInstruction(inst->sign_inst, this);
182 break; 227 break;
183 case Opcode::GetCarryFromOp: 228 case Opcode::GetCarryFromOp:
184 SetPseudoInstruction(value.Inst()->carry_inst, this); 229 SetPseudoInstruction(inst->carry_inst, this);
185 break; 230 break;
186 case Opcode::GetOverflowFromOp: 231 case Opcode::GetOverflowFromOp:
187 SetPseudoInstruction(value.Inst()->overflow_inst, this); 232 SetPseudoInstruction(inst->overflow_inst, this);
188 break; 233 break;
189 default: 234 default:
190 break; 235 break;
@@ -192,20 +237,21 @@ void Inst::Use(const Value& value) {
192} 237}
193 238
194void Inst::UndoUse(const Value& value) { 239void Inst::UndoUse(const Value& value) {
195 --value.Inst()->use_count; 240 Inst* const inst{value.Inst()};
241 --inst->use_count;
196 242
197 switch (op) { 243 switch (op) {
198 case Opcode::GetZeroFromOp: 244 case Opcode::GetZeroFromOp:
199 RemovePseudoInstruction(value.Inst()->zero_inst, Opcode::GetZeroFromOp); 245 RemovePseudoInstruction(inst->zero_inst, Opcode::GetZeroFromOp);
200 break; 246 break;
201 case Opcode::GetSignFromOp: 247 case Opcode::GetSignFromOp:
202 RemovePseudoInstruction(value.Inst()->sign_inst, Opcode::GetSignFromOp); 248 RemovePseudoInstruction(inst->sign_inst, Opcode::GetSignFromOp);
203 break; 249 break;
204 case Opcode::GetCarryFromOp: 250 case Opcode::GetCarryFromOp:
205 RemovePseudoInstruction(value.Inst()->carry_inst, Opcode::GetCarryFromOp); 251 RemovePseudoInstruction(inst->carry_inst, Opcode::GetCarryFromOp);
206 break; 252 break;
207 case Opcode::GetOverflowFromOp: 253 case Opcode::GetOverflowFromOp:
208 RemovePseudoInstruction(value.Inst()->overflow_inst, Opcode::GetOverflowFromOp); 254 RemovePseudoInstruction(inst->overflow_inst, Opcode::GetOverflowFromOp);
209 break; 255 break;
210 default: 256 default:
211 break; 257 break;
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.h b/src/shader_recompiler/frontend/ir/microinstruction.h
index 80baffb2e..ddf0f90a9 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.h
+++ b/src/shader_recompiler/frontend/ir/microinstruction.h
@@ -6,8 +6,8 @@
6 6
7#include <array> 7#include <array>
8#include <cstring> 8#include <cstring>
9#include <span>
10#include <type_traits> 9#include <type_traits>
10#include <utility>
11#include <vector> 11#include <vector>
12 12
13#include <boost/intrusive/list.hpp> 13#include <boost/intrusive/list.hpp>
@@ -25,7 +25,14 @@ constexpr size_t MAX_ARG_COUNT = 4;
25 25
26class Inst : public boost::intrusive::list_base_hook<> { 26class Inst : public boost::intrusive::list_base_hook<> {
27public: 27public:
28 explicit Inst(Opcode op_, u64 flags_) noexcept : op{op_}, flags{flags_} {} 28 explicit Inst(Opcode op_, u64 flags_) noexcept;
29 ~Inst();
30
31 Inst& operator=(const Inst&) = delete;
32 Inst(const Inst&) = delete;
33
34 Inst& operator=(Inst&&) = delete;
35 Inst(Inst&&) = delete;
29 36
30 /// Get the number of uses this instruction has. 37 /// Get the number of uses this instruction has.
31 [[nodiscard]] int UseCount() const noexcept { 38 [[nodiscard]] int UseCount() const noexcept {
@@ -50,26 +57,26 @@ public:
50 [[nodiscard]] bool IsPseudoInstruction() const noexcept; 57 [[nodiscard]] bool IsPseudoInstruction() const noexcept;
51 58
52 /// Determines if all arguments of this instruction are immediates. 59 /// Determines if all arguments of this instruction are immediates.
53 [[nodiscard]] bool AreAllArgsImmediates() const noexcept; 60 [[nodiscard]] bool AreAllArgsImmediates() const;
54 61
55 /// Determines if there is a pseudo-operation associated with this instruction. 62 /// Determines if there is a pseudo-operation associated with this instruction.
56 [[nodiscard]] bool HasAssociatedPseudoOperation() const noexcept; 63 [[nodiscard]] bool HasAssociatedPseudoOperation() const noexcept;
57 /// Gets a pseudo-operation associated with this instruction 64 /// Gets a pseudo-operation associated with this instruction
58 [[nodiscard]] Inst* GetAssociatedPseudoOperation(IR::Opcode opcode); 65 [[nodiscard]] Inst* GetAssociatedPseudoOperation(IR::Opcode opcode);
59 66
60 /// Get the number of arguments this instruction has.
61 [[nodiscard]] size_t NumArgs() const;
62
63 /// Get the type this instruction returns. 67 /// Get the type this instruction returns.
64 [[nodiscard]] IR::Type Type() const; 68 [[nodiscard]] IR::Type Type() const;
65 69
70 /// Get the number of arguments this instruction has.
71 [[nodiscard]] size_t NumArgs() const;
72
66 /// Get the value of a given argument index. 73 /// Get the value of a given argument index.
67 [[nodiscard]] Value Arg(size_t index) const; 74 [[nodiscard]] Value Arg(size_t index) const;
68 /// Set the value of a given argument index. 75 /// Set the value of a given argument index.
69 void SetArg(size_t index, Value value); 76 void SetArg(size_t index, Value value);
70 77
71 /// Get an immutable span to the phi operands. 78 /// Get a pointer to the block of a phi argument.
72 [[nodiscard]] std::span<const std::pair<Block*, Value>> PhiOperands() const noexcept; 79 [[nodiscard]] Block* PhiBlock(size_t index) const;
73 /// Add phi operand to a phi instruction. 80 /// Add phi operand to a phi instruction.
74 void AddPhiOperand(Block* predecessor, const Value& value); 81 void AddPhiOperand(Block* predecessor, const Value& value);
75 82
@@ -87,18 +94,26 @@ public:
87 } 94 }
88 95
89private: 96private:
97 struct NonTriviallyDummy {
98 NonTriviallyDummy() noexcept {}
99 };
100
90 void Use(const Value& value); 101 void Use(const Value& value);
91 void UndoUse(const Value& value); 102 void UndoUse(const Value& value);
92 103
93 IR::Opcode op{}; 104 IR::Opcode op{};
94 int use_count{}; 105 int use_count{};
95 std::array<Value, MAX_ARG_COUNT> args{}; 106 u64 flags{};
107 union {
108 NonTriviallyDummy dummy{};
109 std::array<Value, MAX_ARG_COUNT> args;
110 std::vector<std::pair<Block*, Value>> phi_args;
111 };
96 Inst* zero_inst{}; 112 Inst* zero_inst{};
97 Inst* sign_inst{}; 113 Inst* sign_inst{};
98 Inst* carry_inst{}; 114 Inst* carry_inst{};
99 Inst* overflow_inst{}; 115 Inst* overflow_inst{};
100 std::vector<std::pair<Block*, Value>> phi_operands;
101 u64 flags{};
102}; 116};
117static_assert(sizeof(Inst) <= 128, "Inst size unintentionally increased its size");
103 118
104} // namespace Shader::IR 119} // namespace Shader::IR
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index 6eb105d92..82b04f37c 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -3,9 +3,9 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5// opcode name, return type, arg1 type, arg2 type, arg3 type, arg4 type, ... 5// opcode name, return type, arg1 type, arg2 type, arg3 type, arg4 type, ...
6OPCODE(Phi, Opaque, )
6OPCODE(Void, Void, ) 7OPCODE(Void, Void, )
7OPCODE(Identity, Opaque, Opaque, ) 8OPCODE(Identity, Opaque, Opaque, )
8OPCODE(Phi, Opaque, /*todo*/ )
9 9
10// Control flow 10// Control flow
11OPCODE(Branch, Void, Label, ) 11OPCODE(Branch, Void, Label, )
diff --git a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp
index 7713e3ba9..15a9db90a 100644
--- a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp
+++ b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp
@@ -104,32 +104,34 @@ private:
104 val = ReadVariable(variable, preds.front()); 104 val = ReadVariable(variable, preds.front());
105 } else { 105 } else {
106 // Break potential cycles with operandless phi 106 // Break potential cycles with operandless phi
107 val = IR::Value{&*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; 107 IR::Inst& phi_inst{*block->PrependNewInst(block->begin(), IR::Opcode::Phi)};
108 val = IR::Value{&phi_inst};
108 WriteVariable(variable, block, val); 109 WriteVariable(variable, block, val);
109 val = AddPhiOperands(variable, val, block); 110 val = AddPhiOperands(variable, phi_inst, block);
110 } 111 }
111 WriteVariable(variable, block, val); 112 WriteVariable(variable, block, val);
112 return val; 113 return val;
113 } 114 }
114 115
115 IR::Value AddPhiOperands(auto variable, const IR::Value& phi, IR::Block* block) { 116 IR::Value AddPhiOperands(auto variable, IR::Inst& phi, IR::Block* block) {
116 for (IR::Block* const pred : block->ImmediatePredecessors()) { 117 for (IR::Block* const pred : block->ImmediatePredecessors()) {
117 phi.Inst()->AddPhiOperand(pred, ReadVariable(variable, pred)); 118 phi.AddPhiOperand(pred, ReadVariable(variable, pred));
118 } 119 }
119 return TryRemoveTrivialPhi(phi, block, UndefOpcode(variable)); 120 return TryRemoveTrivialPhi(phi, block, UndefOpcode(variable));
120 } 121 }
121 122
122 IR::Value TryRemoveTrivialPhi(const IR::Value& phi, IR::Block* block, IR::Opcode undef_opcode) { 123 IR::Value TryRemoveTrivialPhi(IR::Inst& phi, IR::Block* block, IR::Opcode undef_opcode) {
123 IR::Value same; 124 IR::Value same;
124 for (const auto& pair : phi.Inst()->PhiOperands()) { 125 const size_t num_args{phi.NumArgs()};
125 const IR::Value& op{pair.second}; 126 for (size_t arg_index = 0; arg_index < num_args; ++arg_index) {
126 if (op == same || op == phi) { 127 const IR::Value& op{phi.Arg(arg_index)};
128 if (op == same || op == IR::Value{&phi}) {
127 // Unique value or self-reference 129 // Unique value or self-reference
128 continue; 130 continue;
129 } 131 }
130 if (!same.IsEmpty()) { 132 if (!same.IsEmpty()) {
131 // The phi merges at least two values: not trivial 133 // The phi merges at least two values: not trivial
132 return phi; 134 return IR::Value{&phi};
133 } 135 }
134 same = op; 136 same = op;
135 } 137 }
@@ -139,7 +141,7 @@ private:
139 same = IR::Value{&*block->PrependNewInst(first_not_phi, undef_opcode)}; 141 same = IR::Value{&*block->PrependNewInst(first_not_phi, undef_opcode)};
140 } 142 }
141 // Reroute all uses of phi to same and remove phi 143 // Reroute all uses of phi to same and remove phi
142 phi.Inst()->ReplaceUsesWith(same); 144 phi.ReplaceUsesWith(same);
143 // TODO: Try to recursively remove all phi users, which might have become trivial 145 // TODO: Try to recursively remove all phi users, which might have become trivial
144 return same; 146 return same;
145 } 147 }
diff --git a/src/shader_recompiler/object_pool.h b/src/shader_recompiler/object_pool.h
index 7c65bbd92..a573add32 100644
--- a/src/shader_recompiler/object_pool.h
+++ b/src/shader_recompiler/object_pool.h
@@ -6,6 +6,7 @@
6 6
7#include <memory> 7#include <memory>
8#include <type_traits> 8#include <type_traits>
9#include <utility>
9 10
10namespace Shader { 11namespace Shader {
11 12
@@ -31,14 +32,12 @@ public:
31 32
32 void ReleaseContents() { 33 void ReleaseContents() {
33 Chunk* chunk{&root}; 34 Chunk* chunk{&root};
34 if (chunk) { 35 while (chunk) {
35 const size_t free_objects{chunk->free_objects}; 36 if (chunk->free_objects == chunk_size) {
36 if (free_objects == chunk_size) {
37 break; 37 break;
38 } 38 }
39 chunk->free_objects = chunk_size; 39 for (; chunk->free_objects < chunk_size; ++chunk->free_objects) {
40 for (size_t obj_id = free_objects; obj_id < chunk_size; ++obj_id) { 40 chunk->storage[chunk->free_objects].object.~T();
41 chunk->storage[obj_id].object.~T();
42 } 41 }
43 chunk = chunk->next.get(); 42 chunk = chunk->next.get();
44 } 43 }