3131
3232#include < libsolutil/Numeric.h>
3333
34- #include < range/v3/view/iota.hpp>
35- #include < range/v3/view/transform.hpp>
36-
3734#include < cstdint>
3835#include < limits>
3936#include < map>
@@ -48,6 +45,8 @@ class InstructionStore
4845 // / Sentinel value for `Inst::payloadIndex` when the opcode has no side-table payload.
4946 static constexpr std::uint32_t NoPayload = std::numeric_limits<std::uint32_t >::max();
5047
48+ using NumReturnsSizeType = std::uint8_t ;
49+
5150 struct BuiltinCall
5251 {
5352 BuiltinHandle builtin;
@@ -58,20 +57,21 @@ class InstructionStore
5857 {
5958 FunctionGraphID graphID;
6059 bool canContinue;
60+ std::size_t numReturns;
6161 };
6262 struct Inst
6363 {
6464 InstOpcode opcode;
65- ValueId::OutputSize numOutputs;
6665 std::uint32_t payloadIndex = NoPayload;
6766 BlockId block{};
68- std::vector<ValueId > inputs{};
67+ std::vector<InstId > inputs{};
6968
7069 constexpr bool isPhi () const noexcept { return opcode == InstOpcode::Phi; }
7170 constexpr bool isUpsilon () const noexcept { return opcode == InstOpcode::Upsilon; }
7271 constexpr bool isLiteral () const noexcept { return opcode == InstOpcode::Const; }
7372 constexpr bool isUnreachable () const noexcept { return opcode == InstOpcode::Unreachable; }
7473 constexpr bool isFunctionArg () const noexcept { return opcode == InstOpcode::FunctionArg; }
74+ constexpr bool isProjection () const noexcept { return opcode == InstOpcode::Projection; }
7575 // / Operation = a Call or BuiltinCall.
7676 constexpr bool isOperation () const noexcept
7777 {
@@ -91,23 +91,11 @@ class InstructionStore
9191 std::size_t numInsts () const { return m_insts.size (); }
9292 std::vector<Inst> const & instructions () const { return m_insts; }
9393
94- static auto outputsOf (InstId const _id, ValueId::OutputSize const _numOutputs)
95- {
96- return
97- ranges::views::iota (ValueId::OutputSize{0 }, _numOutputs) |
98- ranges::views::transform ([_id](ValueId::OutputSize const _pos) { return ValueId{_id, _pos}; });
99- }
100-
101- auto instOutputs (InstId const _id) const
102- {
103- return outputsOf (_id, inst (_id).numOutputs );
104- }
105-
106- // / Returns the opcode category for a given ValueId.
107- InstOpcode kindOf (ValueId const _v) const { return inst (_v.instId ()).opcode ; }
94+ // / Returns the opcode category for a given InstId.
95+ InstOpcode kindOf (InstId const _id) const { return inst (_id).opcode ; }
10896
10997 // / Returns the phi targeted by an Upsilon Inst.
110- ValueId upsilonPhi (InstId const _id) const { return payloadAs<InstOpcode::Upsilon>(_id, m_upsilonPhis); }
98+ InstId upsilonPhi (InstId const _id) const { return payloadAs<InstOpcode::Upsilon>(_id, m_upsilonPhis); }
11199
112100 // / Returns the u256 payload of a Const Inst.
113101 u256 const & literalPayload (InstId const _id) const { return payloadAs<InstOpcode::Const>(_id, m_literalPayloads); }
@@ -116,22 +104,35 @@ class InstructionStore
116104
117105 Call const & callPayload (InstId const _id) const { return payloadAs<InstOpcode::Call>(_id, m_callPayloads); }
118106
107+ // / Returns the projection index of a Projection Inst
108+ NumReturnsSizeType projectionIndex (InstId const _id) const
109+ {
110+ Inst const & projectionInst = inst (_id);
111+ yulAssert (projectionInst.opcode == InstOpcode::Projection);
112+ yulAssert (projectionInst.inputs .size () == 1 );
113+ InstId const producer = projectionInst.inputs .front ();
114+ yulAssert (_id.value > producer.value );
115+ std::size_t const offset = static_cast <std::size_t >(_id.value ) - static_cast <std::size_t >(producer.value ) - 1 ;
116+ yulAssert (offset <= std::numeric_limits<NumReturnsSizeType>::max ());
117+ return static_cast <NumReturnsSizeType>(offset);
118+ }
119+
119120 // / Allocates a new Phi Inst defined in `_definingBlock`. Returns the new InstId.
120121 InstId appendPhi (BlockId const _definingBlock)
121122 {
122- return appendInst (Inst{InstOpcode::Phi, 1 , NoPayload, _definingBlock, {}});
123+ return appendInst (Inst{InstOpcode::Phi, NoPayload, _definingBlock, {}});
123124 }
124125
125126 // / Allocates a new FunctionArg Inst defined in `_entryBlock`. Returns the new InstId.
126127 InstId appendFunctionArg (BlockId const _entryBlock)
127128 {
128- return appendInst (Inst{InstOpcode::FunctionArg, 1 , NoPayload, _entryBlock, {}});
129+ return appendInst (Inst{InstOpcode::FunctionArg, NoPayload, _entryBlock, {}});
129130 }
130131
131132 // / Allocates a new Unreachable Inst. Not pinned to any block (see class comment).
132133 InstId appendUnreachable ()
133134 {
134- return appendInst (Inst{InstOpcode::Unreachable, 1 , NoPayload, BlockId{}, {}});
135+ return appendInst (Inst{InstOpcode::Unreachable, NoPayload, BlockId{}, {}});
135136 }
136137
137138 // / Allocates a new Const Inst with payload `_value`, pinned to `_entryBlock`.
@@ -142,7 +143,7 @@ class InstructionStore
142143 if (auto const it = m_literalDedup.find (_value); it != m_literalDedup.end ())
143144 return it->second ;
144145 auto const payloadIdx = allocPayload (m_literalPayloads, _value);
145- InstId const id = appendInst (Inst{InstOpcode::Const, 1 , payloadIdx, _entryBlock, {}});
146+ InstId const id = appendInst (Inst{InstOpcode::Const, payloadIdx, _entryBlock, {}});
146147 m_literalDedup.emplace (std::move (_value), id);
147148 return id;
148149 }
@@ -151,15 +152,12 @@ class InstructionStore
151152 InstId appendBuiltinCall (
152153 BlockId const _block,
153154 BuiltinCall _payload,
154- std::vector<ValueId> _inputs,
155- std::size_t const _numOutputs
155+ std::vector<InstId> _inputs
156156 )
157157 {
158158 yulAssert (_block.hasValue ());
159- yulAssert (_numOutputs <= std::numeric_limits<ValueId::OutputSize>::max ());
160159 return appendInst (Inst{
161160 InstOpcode::BuiltinCall,
162- static_cast <ValueId::OutputSize>(_numOutputs),
163161 allocPayload (m_builtinPayloads, std::move (_payload)),
164162 _block,
165163 std::move (_inputs)
@@ -170,26 +168,31 @@ class InstructionStore
170168 InstId appendCall (
171169 BlockId const _block,
172170 Call _payload,
173- std::vector<ValueId> _inputs,
174- std::size_t const _numOutputs
171+ std::vector<InstId> _inputs
175172 )
176173 {
177174 yulAssert (_block.hasValue ());
178- yulAssert (_numOutputs <= std::numeric_limits<ValueId::OutputSize>::max ());
179175 return appendInst (Inst{
180176 InstOpcode::Call,
181- static_cast <ValueId::OutputSize>(_numOutputs),
182177 allocPayload (m_callPayloads, std::move (_payload)),
183178 _block,
184179 std::move (_inputs)
185180 });
186181 }
187182
183+ // / Allocates a new Projection Inst projecting output `_index` of `_producer`.
184+ InstId appendProjection (BlockId const _block, InstId const _producer)
185+ {
186+ yulAssert (_block.hasValue ());
187+ yulAssert (_producer.hasValue ());
188+ return appendInst (Inst{InstOpcode::Projection, NoPayload, _block, {_producer}});
189+ }
190+
188191 // / Allocates a new Upsilon Inst targeting `_phi`, feeding `_value` from `_block`.
189- InstId appendUpsilon (BlockId const _block, ValueId const _value, ValueId const _phi)
192+ InstId appendUpsilon (BlockId const _block, InstId const _value, InstId const _phi)
190193 {
191- yulAssert (inst (_phi. instId () ).isPhi ());
192- return appendInst (Inst{InstOpcode::Upsilon, 0 , allocPayload (m_upsilonPhis, _phi), _block, {_value}});
194+ yulAssert (inst (_phi).isPhi ());
195+ return appendInst (Inst{InstOpcode::Upsilon, allocPayload (m_upsilonPhis, _phi), _block, {_value}});
193196 }
194197
195198private:
@@ -225,7 +228,7 @@ class InstructionStore
225228 std::vector<Inst> m_insts;
226229 std::vector<u256> m_literalPayloads;
227230 std::map<u256, InstId> m_literalDedup;
228- std::vector<ValueId > m_upsilonPhis;
231+ std::vector<InstId > m_upsilonPhis;
229232 std::vector<BuiltinCall> m_builtinPayloads;
230233 std::vector<Call> m_callPayloads;
231234};
0 commit comments