1 /* -*- mesa-c++ -*-
2 *
3 * Copyright (c) 2022 Collabora LTD
4 *
5 * Author: Gert Wollny <gert.wollny@collabora.com>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * on the rights to use, copy, modify, merge, publish, distribute, sub
11 * license, and/or sell copies of the Software, and to permit persons to whom
12 * the Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
16 * Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 * USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27 #include "sfn_instr_fetch.h"
28 #include "sfn_valuefactory.h"
29 #include "sfn_defines.h"
30
31 #include <sstream>
32
33 namespace r600 {
34
35 using std::string;
36 using std::istringstream;
37
FetchInstr(EVFetchInstr opcode,const RegisterVec4 & dst,const RegisterVec4::Swizzle & dest_swizzle,PRegister src,uint32_t src_offset,EVFetchType fetch_type,EVTXDataFormat data_format,EVFetchNumFormat num_format,EVFetchEndianSwap endian_swap,uint32_t resource_id,PRegister resource_offset)38 FetchInstr::FetchInstr(EVFetchInstr opcode,
39 const RegisterVec4& dst,
40 const RegisterVec4::Swizzle& dest_swizzle,
41 PRegister src,
42 uint32_t src_offset,
43 EVFetchType fetch_type,
44 EVTXDataFormat data_format,
45 EVFetchNumFormat num_format,
46 EVFetchEndianSwap endian_swap,
47 uint32_t resource_id,
48 PRegister resource_offset):
49 InstrWithVectorResult(dst, dest_swizzle),
50 m_opcode(opcode),
51 m_src(src),
52 m_src_offset(src_offset),
53 m_fetch_type(fetch_type),
54 m_data_format(data_format),
55 m_num_format(num_format),
56 m_endian_swap(endian_swap),
57 m_resource_id(resource_id),
58 m_resource_offset(resource_offset),
59 m_mega_fetch_count(0),
60 m_array_base(0),
61 m_array_size(0),
62 m_elm_size(0)
63 {
64 switch (m_opcode) {
65 case vc_fetch :
66 m_opname ="VFETCH";
67 break;
68 case vc_semantic :
69 m_opname = "FETCH_SEMANTIC";
70 break;
71 case vc_get_buf_resinfo :
72 set_print_skip(mfc);
73 set_print_skip(fmt);
74 set_print_skip(ftype);
75 m_opname = "GET_BUF_RESINFO";
76 break;
77 case vc_read_scratch :
78 m_opname = "READ_SCRATCH";
79 break;
80 default:
81 unreachable("Unknwon fetch instruction");
82 }
83
84 if (m_src)
85 m_src->add_use(this);
86
87 if (m_resource_offset && m_resource_offset->as_register())
88 m_resource_offset->as_register()->add_use(this);
89 }
90
accept(ConstInstrVisitor & visitor) const91 void FetchInstr::accept(ConstInstrVisitor& visitor) const
92 {
93 visitor.visit(*this);
94 }
95
accept(InstrVisitor & visitor)96 void FetchInstr::accept(InstrVisitor& visitor)
97 {
98 visitor.visit(this);
99 }
100
is_equal_to(const FetchInstr & rhs) const101 bool FetchInstr::is_equal_to(const FetchInstr& rhs) const
102 {
103 if (m_src) {
104 if (rhs.m_src) {
105 if (!m_src->equal_to(*rhs.m_src))
106 return false;
107 } else
108 return false;
109 } else if (rhs.m_src)
110 return false;
111
112 if (!comp_dest(rhs.dst(), rhs.all_dest_swizzle()))
113 return false;
114
115 if (m_tex_flags != rhs.m_tex_flags)
116 return false;
117
118 if (m_resource_offset && rhs.m_resource_offset) {
119 if (!m_resource_offset->equal_to(*rhs.m_resource_offset))
120 return false;
121 } else if (!(!!m_resource_offset == !!rhs.m_resource_offset))
122 return false;
123
124 return m_opcode == rhs.m_opcode &&
125 m_src_offset == rhs.m_src_offset &&
126 m_fetch_type == rhs.m_fetch_type &&
127 m_data_format == rhs.m_data_format &&
128 m_num_format == rhs.m_num_format &&
129 m_endian_swap == rhs.m_endian_swap &&
130 m_resource_id == rhs.m_resource_id &&
131 m_mega_fetch_count == rhs.m_mega_fetch_count &&
132 m_array_base == rhs.m_array_base &&
133 m_array_size == rhs.m_array_size &&
134 m_elm_size == rhs.m_elm_size;
135 }
136
propagate_death()137 bool FetchInstr::propagate_death()
138 {
139 auto reg = m_src->as_register();
140 if (reg)
141 reg->del_use(this);
142 return true;
143 }
144
replace_source(PRegister old_src,PVirtualValue new_src)145 bool FetchInstr::replace_source(PRegister old_src, PVirtualValue new_src)
146 {
147 bool success = false;
148 auto new_reg = new_src->as_register();
149 if (new_reg) {
150 if (old_src->equal_to(*m_src)) {
151 m_src->del_use(this);
152 m_src = new_reg;
153 new_reg->add_use(this);
154 success = true;
155 }
156 if (m_resource_offset && old_src->equal_to(*m_resource_offset)) {
157 m_resource_offset->del_use(this);
158 m_resource_offset = new_reg;
159 new_reg->add_use(this);
160 success = true;
161 }
162 }
163 return success;
164 }
165
do_ready() const166 bool FetchInstr::do_ready() const
167 {
168 for (auto i: required_instr()) {
169 if (!i->is_scheduled())
170 return false;
171 }
172
173 bool result = m_src && m_src->ready(block_id(), index());
174 if (m_resource_offset) {
175 auto r = m_resource_offset->as_register();
176 if (r)
177 result &= r->ready(block_id(), index());
178 }
179 return result;
180 }
181
do_print(std::ostream & os) const182 void FetchInstr::do_print(std::ostream& os) const
183 {
184 os << m_opname << ' ';
185
186 print_dest(os);
187
188 os << " :";
189
190 if (m_opcode != vc_get_buf_resinfo) {
191
192 if (m_src && m_src->chan() < 7) {
193 os << " " << *m_src;
194 if (m_src_offset)
195 os << " + " << m_src_offset << "b";
196 }
197 }
198
199 if (m_opcode != vc_read_scratch)
200 os << " RID:" << m_resource_id;
201
202 if (m_resource_offset) {
203 os << " + ";
204 m_resource_offset->print(os);
205 }
206
207 if (!m_skip_print.test(ftype)) {
208 switch (m_fetch_type) {
209 case vertex_data : os << " VERTEX"; break;
210 case instance_data : os << " INSTANCE_DATA"; break;
211 case no_index_offset : os << " NO_IDX_OFFSET"; break;
212 default:
213 unreachable("Unknwon fetch instruction type");
214 }
215 }
216
217 if (!m_skip_print.test(fmt)) {
218 os << " FMT(";
219 auto fmt = s_data_format_map.find(m_data_format);
220 if (fmt != s_data_format_map.end())
221 os << fmt->second << ",";
222 else
223 unreachable("unknwon data format");
224
225 if (m_tex_flags.test(format_comp_signed))
226 os << "S";
227 else
228 os << "U";
229
230 switch (m_num_format) {
231 case vtx_nf_norm : os << "NORM"; break;
232 case vtx_nf_int : os << "INT"; break;
233 case vtx_nf_scaled: os << "SCALED"; break;
234 default:
235 unreachable("Unknwon number format");
236 }
237
238 os << ")";
239 }
240
241 if (m_array_base) {
242 if (m_opcode != vc_read_scratch)
243 os << " BASE:" << m_array_base;
244 else
245 os << " L[0x" << std::uppercase << std::hex << m_array_base << std::dec << "]";
246 }
247
248 if (m_array_size)
249 os << " SIZE:" << m_array_size + 1;
250
251 if (m_tex_flags.test(is_mega_fetch) && !m_skip_print.test(mfc))
252 os << " MFC:" << m_mega_fetch_count;
253
254 if (m_elm_size)
255 os << " ES:" << m_elm_size;
256
257 if (m_tex_flags.test(fetch_whole_quad)) os << " WQ";
258 if (m_tex_flags.test(use_const_field)) os << " UCF";
259 if (m_tex_flags.test(srf_mode)) os << " SRF";
260 if (m_tex_flags.test(buf_no_stride)) os << " BNS";
261 if (m_tex_flags.test(alt_const)) os << " AC";
262 if (m_tex_flags.test(use_tc)) os << " TC";
263 if (m_tex_flags.test(vpm)) os << " VPM";
264 if (m_tex_flags.test(uncached) && m_opcode != vc_read_scratch) os << " UNCACHED";
265 if (m_tex_flags.test(indexed) && m_opcode != vc_read_scratch) os << " INDEXED";
266 }
267
from_string(std::istream & is,ValueFactory & vf)268 Instr::Pointer FetchInstr::from_string(std::istream& is, ValueFactory& vf)
269 {
270 return from_string_impl(is, vc_fetch, vf);
271 }
272
from_string_impl(std::istream & is,EVFetchInstr opcode,ValueFactory & vf)273 Instr::Pointer FetchInstr::from_string_impl(std::istream& is, EVFetchInstr opcode, ValueFactory& vf)
274 {
275 std::string deststr;
276 is >> deststr;
277
278 RegisterVec4::Swizzle dst_swz;
279 auto dest_reg = vf.dest_vec4_from_string(deststr, dst_swz, pin_group);
280
281 char help;
282 is >> help;
283 assert(help == ':');
284
285 string srcstr;
286 is >> srcstr;
287
288 std::cerr << "Get source " << srcstr << "\n";
289
290 auto src_reg = vf.src_from_string(srcstr)->as_register();
291 assert(src_reg);
292
293 string res_id_str;
294 string next;
295 is >> next;
296
297 int src_offset_val = 0;
298
299 if (next == "+") {
300 is >> src_offset_val;
301 is >> help;
302 assert(help == 'b');
303 is >> res_id_str;
304 } else {
305 res_id_str = next;
306 }
307
308 int res_id = int_from_string_with_prefix(res_id_str, "RID:");
309
310 string fetch_type_str;
311 is >> fetch_type_str;
312
313 EVFetchType fetch_type = vertex_data;
314 if (fetch_type_str == "VERTEX") {
315 fetch_type = vertex_data;
316 } else {
317 assert("Fetch type not yet implemented");
318 }
319
320 string format_str;
321 is >> format_str;
322
323 assert(!strncmp(format_str.c_str(), "FMT(", 4));
324 string data_format;
325 string num_format_str;
326
327 istringstream fmt_stream(format_str.substr(4));
328 bool is_num_fmr = false;
329 assert(!fmt_stream.eof());
330
331 do {
332 char c;
333 fmt_stream >> c;
334
335 if (c == ',') {
336 is_num_fmr = true;
337 continue;
338 }
339
340 if (!is_num_fmr)
341 data_format.append(1, c);
342 else
343 num_format_str.append(1, c);
344 } while (!fmt_stream.eof());
345
346 EVTXDataFormat fmt = fmt_invalid;
347
348 for (auto& [f, name] : s_data_format_map) {
349 if (data_format == name) {
350 fmt = f;
351 break;
352 }
353 }
354
355 assert(fmt != fmt_invalid);
356
357 bool fmt_signed = num_format_str[0] == 'S';
358 assert(fmt_signed || num_format_str[0] == 'U');
359
360 size_t num_format_end = num_format_str.find(')');
361 num_format_str = num_format_str.substr(1, num_format_end - 1) ;
362
363 EVFetchNumFormat num_fmt;
364 if (num_format_str == "NORM")
365 num_fmt = vtx_nf_norm;
366 else if (num_format_str == "INT")
367 num_fmt = vtx_nf_int;
368 else if (num_format_str == "SCALED")
369 num_fmt = vtx_nf_scaled;
370 else {
371 std::cerr << "Number format: '" << num_format_str << "' : ";
372 unreachable("Unknown number format");
373 }
374
375 auto fetch = new FetchInstr(opcode, dest_reg, dst_swz,
376 src_reg, src_offset_val, fetch_type, fmt, num_fmt,
377 vtx_es_none, res_id, nullptr);
378 if (fmt_signed)
379 fetch->set_fetch_flag(format_comp_signed);
380
381 while (!is.eof() && is.good()) {
382 std::string next_token;
383 is >> next_token;
384
385 if (next_token.empty())
386 break;
387
388 if (next_token.find(':') != string::npos) {
389 fetch->set_param_from_string(next_token);
390 } else {
391 fetch->set_flag_from_string(next_token);
392 }
393 }
394
395 return fetch;
396 }
397
set_param_from_string(const std::string & token)398 void FetchInstr::set_param_from_string(const std::string& token)
399 {
400 if (token.substr(0,4) == "MFC:")
401 set_mfc(int_from_string_with_prefix(token, "MFC:"));
402 else if (token.substr(0,5) == "ARRB:")
403 set_array_base(int_from_string_with_prefix(token, "ARRB:"));
404 else if (token.substr(0,5) == "ARRS:")
405 set_array_size(int_from_string_with_prefix(token, "ARRS:"));
406 else if (token.substr(0,3) == "ES:")
407 set_element_size(int_from_string_with_prefix(token, "ES:"));
408 else {
409 std::cerr << "Token '" << token << "': ";
410 unreachable("Unknown token in fetch param list");
411 }
412 }
413
set_flag_from_string(const std::string & token)414 void FetchInstr::set_flag_from_string(const std::string& token)
415 {
416 auto flag = s_flag_map.find(token.c_str());
417 if (flag != s_flag_map.end())
418 set_fetch_flag(flag->second);
419 else {
420 std::cerr << "Token: " << token << " : ";
421 unreachable("Unknown token in fetch flag list");
422 }
423 }
424
425
426 const std::map<const char *, FetchInstr::EFlags> FetchInstr::s_flag_map = {
427 {"WQ", fetch_whole_quad},
428 {"UCF", use_const_field},
429 {"SRF", srf_mode},
430 {"BNS", buf_no_stride},
431 {"AC", alt_const},
432 {"TC", use_tc},
433 {"VPM", vpm},
434 {"UNCACHED", uncached},
435 {"INDEXED", indexed}
436 };
437
438 const std::map<EVTXDataFormat, const char *> FetchInstr::s_data_format_map = {
439 {fmt_invalid, "INVALID"},
440 {fmt_8, "8"},
441 {fmt_4_4, "4_4"},
442 {fmt_3_3_2, "3_3_2"},
443 {fmt_reserved_4, "RESERVED_4"},
444 {fmt_16, "16"},
445 {fmt_16_float, "16F"},
446 {fmt_8_8, "8_8"},
447 {fmt_5_6_5, "5_6_5"},
448 {fmt_6_5_5, "6_5_5"},
449 {fmt_1_5_5_5, "1_5_5_5"},
450 {fmt_4_4_4_4, "4_4_4_4"},
451 {fmt_5_5_5_1, "5_5_5_1"},
452 {fmt_32, "32"},
453 {fmt_32_float, "32F"},
454 {fmt_16_16, "16_16"},
455 {fmt_16_16_float, "16_16F"},
456 {fmt_8_24, "8_24"},
457 {fmt_8_24_float, "8_24F"},
458 {fmt_24_8, "24_8"},
459 {fmt_24_8_float, "24_8F"},
460 {fmt_10_11_11, "10_11_11"},
461 {fmt_10_11_11_float, "10_11_11F"},
462 {fmt_11_11_10, "11_11_10"},
463 {fmt_10_11_11_float, "11_11_10F"},
464 {fmt_2_10_10_10, "2_10_10_10"},
465 {fmt_8_8_8_8, "8_8_8_8"},
466 {fmt_10_10_10_2, "10_10_10_2"},
467 {fmt_x24_8_32_float, "X24_8_32F"},
468 {fmt_32_32, "32_32"},
469 {fmt_32_32_float, "32_32F"},
470 {fmt_16_16_16_16, "16_16_16_16"},
471 {fmt_16_16_16_16_float, "16_16_16_16F"},
472 {fmt_reserved_33, "RESERVED_33"},
473 {fmt_32_32_32_32, "32_32_32_32"},
474 {fmt_32_32_32_32_float, "32_32_32_32F"},
475 {fmt_reserved_36, "RESERVED_36"},
476 {fmt_1, "1"},
477 {fmt_1_reversed, "1_REVERSED"},
478 {fmt_gb_gr, "GB_GR"},
479 {fmt_bg_rg, "BG_RG"},
480 {fmt_32_as_8, "32_AS_8"},
481 {fmt_32_as_8_8, "32_AS_8_8"},
482 {fmt_5_9_9_9_sharedexp, "5_9_9_9_SHAREDEXP"},
483 {fmt_8_8_8, "8_8_8"},
484 {fmt_16_16_16, "16_16_16"},
485 {fmt_16_16_16_float, "16_16_16F"},
486 {fmt_32_32_32, "32_32_32"},
487 {fmt_32_32_32_float, "32_32_32F"},
488 {fmt_bc1, "BC1"},
489 {fmt_bc2, "BC2"},
490 {fmt_bc3, "BC3"},
491 {fmt_bc4, "BC4"},
492 {fmt_bc5, "BC5"},
493 {fmt_apc0, "APC0"},
494 {fmt_apc1, "APC1"},
495 {fmt_apc2, "APC2"},
496 {fmt_apc3, "APC3"},
497 {fmt_apc4, "APC4"},
498 {fmt_apc5, "APC5"},
499 {fmt_apc6, "APC6"},
500 {fmt_apc7, "APC7"},
501 {fmt_ctx1, "CTX1"},
502 {fmt_reserved_63, "RESERVED_63"}
503 };
504
505
QueryBufferSizeInstr(const RegisterVec4 & dst,const RegisterVec4::Swizzle & dst_swz,uint32_t resid)506 QueryBufferSizeInstr::QueryBufferSizeInstr(const RegisterVec4& dst,
507 const RegisterVec4::Swizzle& dst_swz,
508 uint32_t resid):
509 FetchInstr(vc_get_buf_resinfo,
510 dst, dst_swz,
511 new Register( 0, 7, pin_fully),
512 0,
513 no_index_offset,
514 fmt_32_32_32_32,
515 vtx_nf_norm,
516 vtx_es_none,
517 resid,
518 nullptr)
519 {
520 set_fetch_flag(format_comp_signed);
521 set_print_skip(mfc);
522 set_print_skip(fmt);
523 set_print_skip(ftype);
524 }
525
from_string(std::istream & is,ValueFactory & vf)526 Instr::Pointer QueryBufferSizeInstr::from_string(std::istream& is, ValueFactory& vf)
527 {
528 std::string deststr, res_id_str;
529 is >> deststr;
530
531 char help;
532 is >> help;
533 assert(help == ':');
534
535 is >> res_id_str;
536
537 RegisterVec4::Swizzle dst_swz;
538 auto dst = vf.dest_vec4_from_string(deststr, dst_swz, pin_group);
539 int res_id = int_from_string_with_prefix(res_id_str, "RID:");
540
541 return new QueryBufferSizeInstr( dst, dst_swz, res_id);
542 }
543
LoadFromBuffer(const RegisterVec4 & dst,const RegisterVec4::Swizzle & dst_swizzle,PRegister addr,uint32_t addr_offset,uint32_t resid,PRegister res_offset,EVTXDataFormat data_format)544 LoadFromBuffer::LoadFromBuffer(const RegisterVec4& dst, const RegisterVec4::Swizzle& dst_swizzle,
545 PRegister addr, uint32_t addr_offset,
546 uint32_t resid, PRegister res_offset, EVTXDataFormat data_format):
547 FetchInstr(vc_fetch, dst, dst_swizzle, addr, addr_offset, no_index_offset,
548 data_format, vtx_nf_scaled, vtx_es_none, resid, res_offset)
549 {
550 set_fetch_flag(format_comp_signed);
551 set_mfc(16);
552 override_opname("LOAD_BUF");
553 set_print_skip(mfc);
554 set_print_skip(fmt);
555 set_print_skip(ftype);
556 }
557
from_string(std::istream & is,ValueFactory & vf)558 Instr::Pointer LoadFromBuffer::from_string(std::istream& is, ValueFactory& vf)
559 {
560 std::string deststr;
561 is >> deststr;
562
563 RegisterVec4::Swizzle dst_swz;
564 auto dst = vf.dest_vec4_from_string(deststr, dst_swz, pin_group);
565
566 char help;
567 is >> help;
568 assert(help == ':');
569
570 string addrstr;
571 is >> addrstr;
572 auto addr_reg = vf.src_from_string(addrstr)->as_register();
573
574 string res_id_str;
575 string next;
576 is >> next;
577
578 int addr_offset_val = 0;
579
580 if (next == "+") {
581 is >> addr_offset_val;
582 is >> help;
583 assert(help == 'b');
584 is >> res_id_str;
585 } else {
586 res_id_str = next;
587 }
588
589 int res_id = int_from_string_with_prefix(res_id_str, "RID:");
590
591 next.clear();
592 is >> next;
593 PRegister res_offset = nullptr;
594 if (next == "+") {
595 string res_offset_str;
596 is >> res_offset_str;
597 res_offset = vf.src_from_string(res_offset_str)->as_register();
598 }
599
600 auto fetch = new LoadFromBuffer( dst, dst_swz,
601 addr_reg, addr_offset_val,
602 res_id, res_offset, fmt_32_32_32_32_float);
603 is >> next;
604 if (next == "SRF")
605 fetch->set_fetch_flag(srf_mode);
606
607 return fetch;
608 }
609
610 class AddrResolver: public RegisterVisitor {
611 public:
AddrResolver(LoadFromScratch * lfs)612 AddrResolver(LoadFromScratch *lfs) : m_lfs(lfs) {}
613
visit(Register & value)614 void visit(Register& value) {
615 m_lfs->set_fetch_flag(FetchInstr::indexed);
616 m_lfs->set_src(&value);
617 value.add_use(m_lfs);
618 }
visit(LocalArray & value)619 void visit(LocalArray& value) {assert(0);(void)value;}
visit(LocalArrayValue & value)620 void visit(LocalArrayValue& value) {assert(0);(void)value;}
visit(UniformValue & value)621 void visit(UniformValue& value) {assert(0);(void)value;}
visit(LiteralConstant & value)622 void visit(LiteralConstant& value) {
623 m_lfs->set_array_base(value.value());
624 m_lfs->set_src(new Register( 0, 7, pin_none));
625 }
visit(InlineConstant & value)626 void visit(InlineConstant& value) {assert(0);(void)value;}
627
628 LoadFromScratch *m_lfs;
629 };
630
631
632
LoadFromScratch(const RegisterVec4 & dst,const RegisterVec4::Swizzle & dst_swz,PVirtualValue addr,uint32_t scratch_size)633 LoadFromScratch::LoadFromScratch(const RegisterVec4& dst, const RegisterVec4::Swizzle& dst_swz, PVirtualValue addr, uint32_t scratch_size):
634 FetchInstr(vc_read_scratch,
635 dst, dst_swz,
636 nullptr,
637 0,
638 no_index_offset,
639 fmt_32_32_32_32,
640 vtx_nf_int,
641 vtx_es_none,
642 0,
643 nullptr)
644 {
645 set_fetch_flag(uncached);
646 set_fetch_flag(wait_ack);
647
648 assert(scratch_size >= 1);
649 set_array_size(scratch_size - 1);
650 set_array_base(0);
651 AddrResolver ar(this);
652 addr->accept(ar);
653
654 set_print_skip(mfc);
655 set_print_skip(fmt);
656 set_print_skip(ftype);
657 set_element_size(3);
658 }
659
from_string(std::istream & is,ValueFactory & vf)660 Instr::Pointer LoadFromScratch::from_string(std::istream& is, ValueFactory &vf)
661 {
662 std::string deststr;
663 is >> deststr;
664
665 RegisterVec4::Swizzle dst_swz;
666 auto dest = vf.dest_vec4_from_string(deststr, dst_swz, pin_group);
667
668 char help;
669 is >> help;
670 assert(help == ':');
671
672 string addrstr;
673 is >> addrstr;
674 auto addr_reg = vf.src_from_string(addrstr);
675
676 string offsetstr;
677 is >> offsetstr;
678 int size = int_from_string_with_prefix(offsetstr, "SIZE:");
679 assert(size >= 1);
680
681 return new LoadFromScratch( dest, dst_swz, addr_reg, size);
682 }
683
684 }
685
686