Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5563 | serge | 1 | /* |
2 | * Copyright 2012 Christoph Bumiller |
||
3 | * |
||
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
||
5 | * copy of this software and associated documentation files (the "Software"), |
||
6 | * to deal in the Software without restriction, including without limitation |
||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||
8 | * and/or sell copies of the Software, and to permit persons to whom the |
||
9 | * Software is furnished to do so, subject to the following conditions: |
||
10 | * |
||
11 | * The above copyright notice and this permission notice shall be included in |
||
12 | * all copies or substantial portions of the Software. |
||
13 | * |
||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
||
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
||
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
||
20 | * OTHER DEALINGS IN THE SOFTWARE. |
||
21 | */ |
||
22 | |||
23 | #include "nv50_ir_target_nvc0.h" |
||
24 | |||
25 | // CodeEmitter for GK110 encoding of the Fermi/Kepler ISA. |
||
26 | |||
27 | namespace nv50_ir { |
||
28 | |||
29 | class CodeEmitterGK110 : public CodeEmitter |
||
30 | { |
||
31 | public: |
||
32 | CodeEmitterGK110(const TargetNVC0 *); |
||
33 | |||
34 | virtual bool emitInstruction(Instruction *); |
||
35 | virtual uint32_t getMinEncodingSize(const Instruction *) const; |
||
36 | virtual void prepareEmission(Function *); |
||
37 | |||
38 | inline void setProgramType(Program::Type pType) { progType = pType; } |
||
39 | |||
40 | private: |
||
41 | const TargetNVC0 *targNVC0; |
||
42 | |||
43 | Program::Type progType; |
||
44 | |||
45 | const bool writeIssueDelays; |
||
46 | |||
47 | private: |
||
48 | void emitForm_21(const Instruction *, uint32_t opc2, uint32_t opc1); |
||
49 | void emitForm_C(const Instruction *, uint32_t opc, uint8_t ctg); |
||
50 | void emitForm_L(const Instruction *, uint32_t opc, uint8_t ctg, Modifier); |
||
51 | |||
52 | void emitPredicate(const Instruction *); |
||
53 | |||
54 | void setCAddress14(const ValueRef&); |
||
55 | void setShortImmediate(const Instruction *, const int s); |
||
56 | void setImmediate32(const Instruction *, const int s, Modifier); |
||
57 | |||
58 | void modNegAbsF32_3b(const Instruction *, const int s); |
||
59 | |||
60 | void emitCondCode(CondCode cc, int pos, uint8_t mask); |
||
61 | void emitInterpMode(const Instruction *); |
||
62 | void emitLoadStoreType(DataType ty, const int pos); |
||
63 | void emitCachingMode(CacheMode c, const int pos); |
||
64 | |||
65 | inline uint8_t getSRegEncoding(const ValueRef&); |
||
66 | |||
67 | void emitRoundMode(RoundMode, const int pos, const int rintPos); |
||
68 | void emitRoundModeF(RoundMode, const int pos); |
||
69 | void emitRoundModeI(RoundMode, const int pos); |
||
70 | |||
71 | void emitNegAbs12(const Instruction *); |
||
72 | |||
73 | void emitNOP(const Instruction *); |
||
74 | |||
75 | void emitLOAD(const Instruction *); |
||
76 | void emitSTORE(const Instruction *); |
||
77 | void emitMOV(const Instruction *); |
||
78 | |||
79 | void emitINTERP(const Instruction *); |
||
80 | void emitPFETCH(const Instruction *); |
||
81 | void emitVFETCH(const Instruction *); |
||
82 | void emitEXPORT(const Instruction *); |
||
83 | void emitOUT(const Instruction *); |
||
84 | |||
85 | void emitUADD(const Instruction *); |
||
86 | void emitFADD(const Instruction *); |
||
87 | void emitIMUL(const Instruction *); |
||
88 | void emitFMUL(const Instruction *); |
||
89 | void emitIMAD(const Instruction *); |
||
90 | void emitISAD(const Instruction *); |
||
91 | void emitFMAD(const Instruction *); |
||
92 | |||
93 | void emitNOT(const Instruction *); |
||
94 | void emitLogicOp(const Instruction *, uint8_t subOp); |
||
95 | void emitPOPC(const Instruction *); |
||
96 | void emitINSBF(const Instruction *); |
||
97 | void emitShift(const Instruction *); |
||
98 | |||
99 | void emitSFnOp(const Instruction *, uint8_t subOp); |
||
100 | |||
101 | void emitCVT(const Instruction *); |
||
102 | void emitMINMAX(const Instruction *); |
||
103 | void emitPreOp(const Instruction *); |
||
104 | |||
105 | void emitSET(const CmpInstruction *); |
||
106 | void emitSLCT(const CmpInstruction *); |
||
107 | void emitSELP(const Instruction *); |
||
108 | |||
109 | void emitTEXBAR(const Instruction *); |
||
110 | void emitTEX(const TexInstruction *); |
||
111 | void emitTEXCSAA(const TexInstruction *); |
||
112 | void emitTXQ(const TexInstruction *); |
||
113 | |||
114 | void emitQUADOP(const Instruction *, uint8_t qOp, uint8_t laneMask); |
||
115 | |||
116 | void emitFlow(const Instruction *); |
||
117 | |||
118 | inline void defId(const ValueDef&, const int pos); |
||
119 | inline void srcId(const ValueRef&, const int pos); |
||
120 | inline void srcId(const ValueRef *, const int pos); |
||
121 | inline void srcId(const Instruction *, int s, const int pos); |
||
122 | |||
123 | inline void srcAddr32(const ValueRef&, const int pos); // address / 4 |
||
124 | |||
125 | inline bool isLIMM(const ValueRef&, DataType ty, bool mod = false); |
||
126 | }; |
||
127 | |||
128 | #define GK110_GPR_ZERO 255 |
||
129 | |||
130 | #define NEG_(b, s) \ |
||
131 | if (i->src(s).mod.neg()) code[(0x##b) / 32] |= 1 << ((0x##b) % 32) |
||
132 | #define ABS_(b, s) \ |
||
133 | if (i->src(s).mod.abs()) code[(0x##b) / 32] |= 1 << ((0x##b) % 32) |
||
134 | |||
135 | #define NOT_(b, s) if (i->src(s).mod & Modifier(NV50_IR_MOD_NOT)) \ |
||
136 | code[(0x##b) / 32] |= 1 << ((0x##b) % 32) |
||
137 | |||
138 | #define FTZ_(b) if (i->ftz) code[(0x##b) / 32] |= 1 << ((0x##b) % 32) |
||
139 | |||
140 | #define SAT_(b) if (i->saturate) code[(0x##b) / 32] |= 1 << ((0x##b) % 32) |
||
141 | |||
142 | #define RND_(b, t) emitRoundMode##t(i->rnd, 0x##b) |
||
143 | |||
144 | #define SDATA(a) ((a).rep()->reg.data) |
||
145 | #define DDATA(a) ((a).rep()->reg.data) |
||
146 | |||
147 | void CodeEmitterGK110::srcId(const ValueRef& src, const int pos) |
||
148 | { |
||
149 | code[pos / 32] |= (src.get() ? SDATA(src).id : GK110_GPR_ZERO) << (pos % 32); |
||
150 | } |
||
151 | |||
152 | void CodeEmitterGK110::srcId(const ValueRef *src, const int pos) |
||
153 | { |
||
154 | code[pos / 32] |= (src ? SDATA(*src).id : GK110_GPR_ZERO) << (pos % 32); |
||
155 | } |
||
156 | |||
157 | void CodeEmitterGK110::srcId(const Instruction *insn, int s, int pos) |
||
158 | { |
||
159 | int r = insn->srcExists(s) ? SDATA(insn->src(s)).id : GK110_GPR_ZERO; |
||
160 | code[pos / 32] |= r << (pos % 32); |
||
161 | } |
||
162 | |||
163 | void CodeEmitterGK110::srcAddr32(const ValueRef& src, const int pos) |
||
164 | { |
||
165 | code[pos / 32] |= (SDATA(src).offset >> 2) << (pos % 32); |
||
166 | } |
||
167 | |||
168 | void CodeEmitterGK110::defId(const ValueDef& def, const int pos) |
||
169 | { |
||
170 | code[pos / 32] |= (def.get() ? DDATA(def).id : GK110_GPR_ZERO) << (pos % 32); |
||
171 | } |
||
172 | |||
173 | bool CodeEmitterGK110::isLIMM(const ValueRef& ref, DataType ty, bool mod) |
||
174 | { |
||
175 | const ImmediateValue *imm = ref.get()->asImm(); |
||
176 | |||
177 | return imm && (imm->reg.data.u32 & ((ty == TYPE_F32) ? 0xfff : 0xfff00000)); |
||
178 | } |
||
179 | |||
180 | void |
||
181 | CodeEmitterGK110::emitRoundMode(RoundMode rnd, const int pos, const int rintPos) |
||
182 | { |
||
183 | bool rint = false; |
||
184 | uint8_t n; |
||
185 | |||
186 | switch (rnd) { |
||
187 | case ROUND_MI: rint = true; /* fall through */ case ROUND_M: n = 1; break; |
||
188 | case ROUND_PI: rint = true; /* fall through */ case ROUND_P: n = 2; break; |
||
189 | case ROUND_ZI: rint = true; /* fall through */ case ROUND_Z: n = 3; break; |
||
190 | default: |
||
191 | rint = rnd == ROUND_NI; |
||
192 | n = 0; |
||
193 | assert(rnd == ROUND_N || rnd == ROUND_NI); |
||
194 | break; |
||
195 | } |
||
196 | code[pos / 32] |= n << (pos % 32); |
||
197 | if (rint && rintPos >= 0) |
||
198 | code[rintPos / 32] |= 1 << (rintPos % 32); |
||
199 | } |
||
200 | |||
201 | void |
||
202 | CodeEmitterGK110::emitRoundModeF(RoundMode rnd, const int pos) |
||
203 | { |
||
204 | uint8_t n; |
||
205 | |||
206 | switch (rnd) { |
||
207 | case ROUND_M: n = 1; break; |
||
208 | case ROUND_P: n = 2; break; |
||
209 | case ROUND_Z: n = 3; break; |
||
210 | default: |
||
211 | n = 0; |
||
212 | assert(rnd == ROUND_N); |
||
213 | break; |
||
214 | } |
||
215 | code[pos / 32] |= n << (pos % 32); |
||
216 | } |
||
217 | |||
218 | void |
||
219 | CodeEmitterGK110::emitRoundModeI(RoundMode rnd, const int pos) |
||
220 | { |
||
221 | uint8_t n; |
||
222 | |||
223 | switch (rnd) { |
||
224 | case ROUND_MI: n = 1; break; |
||
225 | case ROUND_PI: n = 2; break; |
||
226 | case ROUND_ZI: n = 3; break; |
||
227 | default: |
||
228 | n = 0; |
||
229 | assert(rnd == ROUND_NI); |
||
230 | break; |
||
231 | } |
||
232 | code[pos / 32] |= n << (pos % 32); |
||
233 | } |
||
234 | |||
235 | void CodeEmitterGK110::emitCondCode(CondCode cc, int pos, uint8_t mask) |
||
236 | { |
||
237 | uint8_t n; |
||
238 | |||
239 | switch (cc) { |
||
240 | case CC_FL: n = 0x00; break; |
||
241 | case CC_LT: n = 0x01; break; |
||
242 | case CC_EQ: n = 0x02; break; |
||
243 | case CC_LE: n = 0x03; break; |
||
244 | case CC_GT: n = 0x04; break; |
||
245 | case CC_NE: n = 0x05; break; |
||
246 | case CC_GE: n = 0x06; break; |
||
247 | case CC_LTU: n = 0x09; break; |
||
248 | case CC_EQU: n = 0x0a; break; |
||
249 | case CC_LEU: n = 0x0b; break; |
||
250 | case CC_GTU: n = 0x0c; break; |
||
251 | case CC_NEU: n = 0x0d; break; |
||
252 | case CC_GEU: n = 0x0e; break; |
||
253 | case CC_TR: n = 0x0f; break; |
||
254 | case CC_NO: n = 0x10; break; |
||
255 | case CC_NC: n = 0x11; break; |
||
256 | case CC_NS: n = 0x12; break; |
||
257 | case CC_NA: n = 0x13; break; |
||
258 | case CC_A: n = 0x14; break; |
||
259 | case CC_S: n = 0x15; break; |
||
260 | case CC_C: n = 0x16; break; |
||
261 | case CC_O: n = 0x17; break; |
||
262 | default: |
||
263 | n = 0; |
||
264 | assert(!"invalid condition code"); |
||
265 | break; |
||
266 | } |
||
267 | code[pos / 32] |= (n & mask) << (pos % 32); |
||
268 | } |
||
269 | |||
270 | void |
||
271 | CodeEmitterGK110::emitPredicate(const Instruction *i) |
||
272 | { |
||
273 | if (i->predSrc >= 0) { |
||
274 | srcId(i->src(i->predSrc), 18); |
||
275 | if (i->cc == CC_NOT_P) |
||
276 | code[0] |= 8 << 18; // negate |
||
277 | assert(i->getPredicate()->reg.file == FILE_PREDICATE); |
||
278 | } else { |
||
279 | code[0] |= 7 << 18; |
||
280 | } |
||
281 | } |
||
282 | |||
283 | void |
||
284 | CodeEmitterGK110::setCAddress14(const ValueRef& src) |
||
285 | { |
||
286 | const int32_t addr = src.get()->asSym()->reg.data.offset / 4; |
||
287 | |||
288 | code[0] |= (addr & 0x01ff) << 23; |
||
289 | code[1] |= (addr & 0x3e00) >> 9; |
||
290 | } |
||
291 | |||
292 | void |
||
293 | CodeEmitterGK110::setShortImmediate(const Instruction *i, const int s) |
||
294 | { |
||
295 | const uint32_t u32 = i->getSrc(s)->asImm()->reg.data.u32; |
||
296 | const uint64_t u64 = i->getSrc(s)->asImm()->reg.data.u64; |
||
297 | |||
298 | if (i->sType == TYPE_F32) { |
||
299 | assert(!(u32 & 0x00000fff)); |
||
300 | code[0] |= ((u32 & 0x001ff000) >> 12) << 23; |
||
301 | code[1] |= ((u32 & 0x7fe00000) >> 21); |
||
302 | code[1] |= ((u32 & 0x80000000) >> 4); |
||
303 | } else |
||
304 | if (i->sType == TYPE_F64) { |
||
305 | assert(!(u64 & 0x00000fffffffffffULL)); |
||
306 | code[0] |= ((u64 & 0x001ff00000000000ULL) >> 44) << 23; |
||
307 | code[1] |= ((u64 & 0x7fe0000000000000ULL) >> 53); |
||
308 | code[1] |= ((u64 & 0x8000000000000000ULL) >> 36); |
||
309 | } else { |
||
310 | assert((u32 & 0xfff00000) == 0 || (u32 & 0xfff00000) == 0xfff00000); |
||
311 | code[0] |= (u32 & 0x001ff) << 23; |
||
312 | code[1] |= (u32 & 0x7fe00) >> 9; |
||
313 | code[1] |= (u32 & 0x80000) << 8; |
||
314 | } |
||
315 | } |
||
316 | |||
317 | void |
||
318 | CodeEmitterGK110::setImmediate32(const Instruction *i, const int s, |
||
319 | Modifier mod) |
||
320 | { |
||
321 | uint32_t u32 = i->getSrc(s)->asImm()->reg.data.u32; |
||
322 | |||
323 | if (mod) { |
||
324 | ImmediateValue imm(i->getSrc(s)->asImm(), i->sType); |
||
325 | mod.applyTo(imm); |
||
326 | u32 = imm.reg.data.u32; |
||
327 | } |
||
328 | |||
329 | code[0] |= u32 << 23; |
||
330 | code[1] |= u32 >> 9; |
||
331 | } |
||
332 | |||
333 | void |
||
334 | CodeEmitterGK110::emitForm_L(const Instruction *i, uint32_t opc, uint8_t ctg, |
||
335 | Modifier mod) |
||
336 | { |
||
337 | code[0] = ctg; |
||
338 | code[1] = opc << 20; |
||
339 | |||
340 | emitPredicate(i); |
||
341 | |||
342 | defId(i->def(0), 2); |
||
343 | |||
344 | for (int s = 0; s < 3 && i->srcExists(s); ++s) { |
||
345 | switch (i->src(s).getFile()) { |
||
346 | case FILE_GPR: |
||
347 | srcId(i->src(s), s ? 42 : 10); |
||
348 | break; |
||
349 | case FILE_IMMEDIATE: |
||
350 | setImmediate32(i, s, mod); |
||
351 | break; |
||
352 | default: |
||
353 | break; |
||
354 | } |
||
355 | } |
||
356 | } |
||
357 | |||
358 | |||
359 | void |
||
360 | CodeEmitterGK110::emitForm_C(const Instruction *i, uint32_t opc, uint8_t ctg) |
||
361 | { |
||
362 | code[0] = ctg; |
||
363 | code[1] = opc << 20; |
||
364 | |||
365 | emitPredicate(i); |
||
366 | |||
367 | defId(i->def(0), 2); |
||
368 | |||
369 | switch (i->src(0).getFile()) { |
||
370 | case FILE_MEMORY_CONST: |
||
371 | code[1] |= 0x4 << 28; |
||
372 | setCAddress14(i->src(0)); |
||
373 | break; |
||
374 | case FILE_GPR: |
||
375 | code[1] |= 0xc << 28; |
||
376 | srcId(i->src(0), 23); |
||
377 | break; |
||
378 | default: |
||
379 | assert(0); |
||
380 | break; |
||
381 | } |
||
382 | } |
||
383 | |||
384 | // 0x2 for GPR, c[] and 0x1 for short immediate |
||
385 | void |
||
386 | CodeEmitterGK110::emitForm_21(const Instruction *i, uint32_t opc2, |
||
387 | uint32_t opc1) |
||
388 | { |
||
389 | const bool imm = i->srcExists(1) && i->src(1).getFile() == FILE_IMMEDIATE; |
||
390 | |||
391 | int s1 = 23; |
||
392 | if (i->srcExists(2) && i->src(2).getFile() == FILE_MEMORY_CONST) |
||
393 | s1 = 42; |
||
394 | |||
395 | if (imm) { |
||
396 | code[0] = 0x1; |
||
397 | code[1] = opc1 << 20; |
||
398 | } else { |
||
399 | code[0] = 0x2; |
||
400 | code[1] = (0xc << 28) | (opc2 << 20); |
||
401 | } |
||
402 | |||
403 | emitPredicate(i); |
||
404 | |||
405 | defId(i->def(0), 2); |
||
406 | |||
407 | for (int s = 0; s < 3 && i->srcExists(s); ++s) { |
||
408 | switch (i->src(s).getFile()) { |
||
409 | case FILE_MEMORY_CONST: |
||
410 | code[1] &= (s == 2) ? ~(0x4 << 28) : ~(0x8 << 28); |
||
411 | setCAddress14(i->src(s)); |
||
412 | code[1] |= i->getSrc(s)->reg.fileIndex << 5; |
||
413 | break; |
||
414 | case FILE_IMMEDIATE: |
||
415 | setShortImmediate(i, s); |
||
416 | break; |
||
417 | case FILE_GPR: |
||
418 | srcId(i->src(s), s ? ((s == 2) ? 42 : s1) : 10); |
||
419 | break; |
||
420 | default: |
||
421 | // ignore here, can be predicate or flags, but must not be address |
||
422 | break; |
||
423 | } |
||
424 | } |
||
425 | // 0x0 = invalid |
||
426 | // 0xc = rrr |
||
427 | // 0x8 = rrc |
||
428 | // 0x4 = rcr |
||
429 | assert(imm || (code[1] & (0xc << 28))); |
||
430 | } |
||
431 | |||
432 | inline void |
||
433 | CodeEmitterGK110::modNegAbsF32_3b(const Instruction *i, const int s) |
||
434 | { |
||
435 | if (i->src(s).mod.abs()) code[1] &= ~(1 << 27); |
||
436 | if (i->src(s).mod.neg()) code[1] ^= (1 << 27); |
||
437 | } |
||
438 | |||
439 | void |
||
440 | CodeEmitterGK110::emitNOP(const Instruction *i) |
||
441 | { |
||
442 | code[0] = 0x00003c02; |
||
443 | code[1] = 0x85800000; |
||
444 | |||
445 | if (i) |
||
446 | emitPredicate(i); |
||
447 | else |
||
448 | code[0] = 0x001c3c02; |
||
449 | } |
||
450 | |||
451 | void |
||
452 | CodeEmitterGK110::emitFMAD(const Instruction *i) |
||
453 | { |
||
454 | assert(!isLIMM(i->src(1), TYPE_F32)); |
||
455 | |||
456 | emitForm_21(i, 0x0c0, 0x940); |
||
457 | |||
458 | NEG_(34, 2); |
||
459 | SAT_(35); |
||
460 | RND_(36, F); |
||
461 | FTZ_(38); |
||
462 | |||
463 | bool neg1 = (i->src(0).mod ^ i->src(1).mod).neg(); |
||
464 | |||
465 | if (code[0] & 0x1) { |
||
466 | if (neg1) |
||
467 | code[1] ^= 1 << 27; |
||
468 | } else |
||
469 | if (neg1) { |
||
470 | code[1] |= 1 << 19; |
||
471 | } |
||
472 | } |
||
473 | |||
474 | void |
||
475 | CodeEmitterGK110::emitFMUL(const Instruction *i) |
||
476 | { |
||
477 | bool neg = (i->src(0).mod ^ i->src(1).mod).neg(); |
||
478 | |||
479 | assert(i->postFactor >= -3 && i->postFactor <= 3); |
||
480 | |||
481 | if (isLIMM(i->src(1), TYPE_F32)) { |
||
482 | emitForm_L(i, 0x200, 0x2, Modifier(0)); |
||
483 | |||
484 | FTZ_(38); |
||
485 | SAT_(3a); |
||
486 | if (neg) |
||
487 | code[1] ^= 1 << 22; |
||
488 | |||
489 | assert(i->postFactor == 0); |
||
490 | } else { |
||
491 | emitForm_21(i, 0x234, 0xc34); |
||
492 | |||
493 | RND_(2a, F); |
||
494 | FTZ_(2f); |
||
495 | SAT_(35); |
||
496 | |||
497 | if (code[0] & 0x1) { |
||
498 | if (neg) |
||
499 | code[1] ^= 1 << 27; |
||
500 | } else |
||
501 | if (neg) { |
||
502 | code[1] |= 1 << 19; |
||
503 | } |
||
504 | } |
||
505 | } |
||
506 | |||
507 | void |
||
508 | CodeEmitterGK110::emitIMUL(const Instruction *i) |
||
509 | { |
||
510 | assert(!i->src(0).mod.neg() && !i->src(1).mod.neg()); |
||
511 | assert(!i->src(0).mod.abs() && !i->src(1).mod.abs()); |
||
512 | |||
513 | if (isLIMM(i->src(1), TYPE_S32)) { |
||
514 | emitForm_L(i, 0x280, 2, Modifier(0)); |
||
515 | |||
516 | assert(i->subOp != NV50_IR_SUBOP_MUL_HIGH); |
||
517 | |||
518 | if (i->sType == TYPE_S32) |
||
519 | code[1] |= 3 << 25; |
||
520 | } else { |
||
521 | emitForm_21(i, 0x21c, 0xc1c); |
||
522 | |||
523 | if (i->subOp == NV50_IR_SUBOP_MUL_HIGH) |
||
524 | code[1] |= 1 << 10; |
||
525 | if (i->sType == TYPE_S32) |
||
526 | code[1] |= 3 << 11; |
||
527 | } |
||
528 | } |
||
529 | |||
530 | void |
||
531 | CodeEmitterGK110::emitFADD(const Instruction *i) |
||
532 | { |
||
533 | if (isLIMM(i->src(1), TYPE_F32)) { |
||
534 | assert(i->rnd == ROUND_N); |
||
535 | assert(!i->saturate); |
||
536 | |||
537 | emitForm_L(i, 0x400, 0, i->src(1).mod); |
||
538 | |||
539 | FTZ_(3a); |
||
540 | NEG_(3b, 0); |
||
541 | ABS_(39, 0); |
||
542 | } else { |
||
543 | emitForm_21(i, 0x22c, 0xc2c); |
||
544 | |||
545 | FTZ_(2f); |
||
546 | RND_(2a, F); |
||
547 | ABS_(31, 0); |
||
548 | NEG_(33, 0); |
||
549 | |||
550 | if (code[0] & 0x1) { |
||
551 | modNegAbsF32_3b(i, 1); |
||
552 | } else { |
||
553 | ABS_(34, 1); |
||
554 | NEG_(30, 1); |
||
555 | } |
||
556 | } |
||
557 | } |
||
558 | |||
559 | void |
||
560 | CodeEmitterGK110::emitUADD(const Instruction *i) |
||
561 | { |
||
562 | uint8_t addOp = (i->src(0).mod.neg() << 1) | i->src(1).mod.neg(); |
||
563 | |||
564 | if (i->op == OP_SUB) |
||
565 | addOp ^= 1; |
||
566 | |||
567 | assert(!i->src(0).mod.abs() && !i->src(1).mod.abs()); |
||
568 | |||
569 | if (isLIMM(i->src(1), TYPE_S32)) { |
||
570 | emitForm_L(i, 0x400, 1, Modifier((addOp & 1) ? NV50_IR_MOD_NEG : 0)); |
||
571 | |||
572 | if (addOp & 2) |
||
573 | code[1] |= 1 << 27; |
||
574 | |||
575 | assert(!i->defExists(1)); |
||
576 | assert(i->flagsSrc < 0); |
||
577 | |||
578 | SAT_(39); |
||
579 | } else { |
||
580 | emitForm_21(i, 0x208, 0xc08); |
||
581 | |||
582 | assert(addOp != 3); // would be add-plus-one |
||
583 | |||
584 | code[1] |= addOp << 19; |
||
585 | |||
586 | if (i->defExists(1)) |
||
587 | code[1] |= 1 << 18; // write carry |
||
588 | if (i->flagsSrc >= 0) |
||
589 | code[1] |= 1 << 14; // add carry |
||
590 | |||
591 | SAT_(35); |
||
592 | } |
||
593 | } |
||
594 | |||
595 | // TODO: shl-add |
||
596 | void |
||
597 | CodeEmitterGK110::emitIMAD(const Instruction *i) |
||
598 | { |
||
599 | uint8_t addOp = |
||
600 | (i->src(2).mod.neg() << 1) | (i->src(0).mod.neg() ^ i->src(1).mod.neg()); |
||
601 | |||
602 | emitForm_21(i, 0x100, 0xa00); |
||
603 | |||
604 | assert(addOp != 3); |
||
605 | code[1] |= addOp << 26; |
||
606 | |||
607 | if (i->sType == TYPE_S32) |
||
608 | code[1] |= (1 << 19) | (1 << 24); |
||
609 | |||
610 | if (code[0] & 0x1) { |
||
611 | assert(!i->subOp); |
||
612 | SAT_(39); |
||
613 | } else { |
||
614 | if (i->subOp == NV50_IR_SUBOP_MUL_HIGH) |
||
615 | code[1] |= 1 << 25; |
||
616 | SAT_(35); |
||
617 | } |
||
618 | } |
||
619 | |||
620 | void |
||
621 | CodeEmitterGK110::emitISAD(const Instruction *i) |
||
622 | { |
||
623 | assert(i->dType == TYPE_S32 || i->dType == TYPE_U32); |
||
624 | |||
625 | emitForm_21(i, 0x1fc, 0xb74); |
||
626 | |||
627 | if (i->dType == TYPE_S32) |
||
628 | code[1] |= 1 << 19; |
||
629 | } |
||
630 | |||
631 | void |
||
632 | CodeEmitterGK110::emitNOT(const Instruction *i) |
||
633 | { |
||
634 | code[0] = 0x0003fc02; // logop(mov2) dst, 0, not src |
||
635 | code[1] = 0x22003800; |
||
636 | |||
637 | emitPredicate(i); |
||
638 | |||
639 | defId(i->def(0), 2); |
||
640 | |||
641 | switch (i->src(0).getFile()) { |
||
642 | case FILE_GPR: |
||
643 | code[1] |= 0xc << 28; |
||
644 | srcId(i->src(0), 23); |
||
645 | break; |
||
646 | case FILE_MEMORY_CONST: |
||
647 | code[1] |= 0x4 << 28; |
||
648 | setCAddress14(i->src(1)); |
||
649 | break; |
||
650 | default: |
||
651 | assert(0); |
||
652 | break; |
||
653 | } |
||
654 | } |
||
655 | |||
656 | void |
||
657 | CodeEmitterGK110::emitLogicOp(const Instruction *i, uint8_t subOp) |
||
658 | { |
||
659 | assert(!(i->src(0).mod & Modifier(NV50_IR_MOD_NOT))); // XXX: find me |
||
660 | |||
661 | if (isLIMM(i->src(1), TYPE_S32)) { |
||
662 | emitForm_L(i, 0x200, 0, i->src(1).mod); |
||
663 | code[1] |= subOp << 24; |
||
664 | } else { |
||
665 | emitForm_21(i, 0x220, 0xc20); |
||
666 | code[1] |= subOp << 12; |
||
667 | NOT_(2b, 1); |
||
668 | } |
||
669 | assert(!(code[0] & 0x1) || !(i->src(1).mod & Modifier(NV50_IR_MOD_NOT))); |
||
670 | } |
||
671 | |||
672 | void |
||
673 | CodeEmitterGK110::emitPOPC(const Instruction *i) |
||
674 | { |
||
675 | assert(!isLIMM(i->src(1), TYPE_S32, true)); |
||
676 | |||
677 | emitForm_21(i, 0x204, 0xc04); |
||
678 | |||
679 | NOT_(2a, 0); |
||
680 | if (!(code[0] & 0x1)) |
||
681 | NOT_(2b, 1); |
||
682 | } |
||
683 | |||
684 | void |
||
685 | CodeEmitterGK110::emitINSBF(const Instruction *i) |
||
686 | { |
||
687 | emitForm_21(i, 0x1f8, 0xb78); |
||
688 | } |
||
689 | |||
690 | void |
||
691 | CodeEmitterGK110::emitShift(const Instruction *i) |
||
692 | { |
||
693 | const bool sar = i->op == OP_SHR && isSignedType(i->sType); |
||
694 | |||
695 | if (sar) { |
||
696 | emitForm_21(i, 0x214, 0x014); |
||
697 | code[1] |= 1 << 19; |
||
698 | } else |
||
699 | if (i->op == OP_SHR) { |
||
700 | // this is actually RSHF |
||
701 | emitForm_21(i, 0x27c, 0x87c); |
||
702 | code[1] |= GK110_GPR_ZERO << 10; |
||
703 | } else { |
||
704 | // this is actually LSHF |
||
705 | emitForm_21(i, 0x1fc, 0xb7c); |
||
706 | code[1] |= GK110_GPR_ZERO << 10; |
||
707 | } |
||
708 | |||
709 | if (i->subOp == NV50_IR_SUBOP_SHIFT_WRAP) { |
||
710 | if (!sar) |
||
711 | code[1] |= 1 << 21; |
||
712 | // XXX: find wrap modifier for SHR S32 |
||
713 | } |
||
714 | } |
||
715 | |||
716 | void |
||
717 | CodeEmitterGK110::emitPreOp(const Instruction *i) |
||
718 | { |
||
719 | emitForm_21(i, 0x248, -1); |
||
720 | |||
721 | if (i->op == OP_PREEX2) |
||
722 | code[1] |= 1 << 10; |
||
723 | |||
724 | NEG_(30, 0); |
||
725 | ABS_(34, 0); |
||
726 | } |
||
727 | |||
728 | void |
||
729 | CodeEmitterGK110::emitSFnOp(const Instruction *i, uint8_t subOp) |
||
730 | { |
||
731 | code[0] = 0x00000002 | (subOp << 23); |
||
732 | code[1] = 0x84000000; |
||
733 | |||
734 | emitPredicate(i); |
||
735 | |||
736 | defId(i->def(0), 2); |
||
737 | srcId(i->src(0), 10); |
||
738 | |||
739 | NEG_(33, 0); |
||
740 | ABS_(31, 0); |
||
741 | |||
742 | // XXX: find saturate |
||
743 | } |
||
744 | |||
745 | void |
||
746 | CodeEmitterGK110::emitMINMAX(const Instruction *i) |
||
747 | { |
||
748 | uint32_t op2, op1; |
||
749 | |||
750 | switch (i->dType) { |
||
751 | case TYPE_U32: |
||
752 | case TYPE_S32: |
||
753 | op2 = 0x210; |
||
754 | op1 = 0xc10; |
||
755 | break; |
||
756 | case TYPE_F32: |
||
757 | op2 = 0x230; |
||
758 | op1 = 0xc30; |
||
759 | break; |
||
760 | case TYPE_F64: |
||
761 | op2 = 0x228; |
||
762 | op1 = 0xc28; |
||
763 | break; |
||
764 | default: |
||
765 | assert(0); |
||
766 | op2 = 0; |
||
767 | op1 = 0; |
||
768 | break; |
||
769 | } |
||
770 | emitForm_21(i, op2, op1); |
||
771 | |||
772 | if (i->dType == TYPE_S32) |
||
773 | code[1] |= 1 << 19; |
||
774 | code[1] |= (i->op == OP_MIN) ? 0x1c00 : 0x3c00; // [!]pt |
||
775 | |||
776 | FTZ_(2f); |
||
777 | ABS_(31, 0); |
||
778 | NEG_(33, 0); |
||
779 | if (code[0] & 0x1) { |
||
780 | modNegAbsF32_3b(i, 1); |
||
781 | } else { |
||
782 | ABS_(34, 1); |
||
783 | NEG_(30, 1); |
||
784 | } |
||
785 | } |
||
786 | |||
787 | void |
||
788 | CodeEmitterGK110::emitCVT(const Instruction *i) |
||
789 | { |
||
790 | const bool f2f = isFloatType(i->dType) && isFloatType(i->sType); |
||
791 | const bool f2i = !isFloatType(i->dType) && isFloatType(i->sType); |
||
792 | const bool i2f = isFloatType(i->dType) && !isFloatType(i->sType); |
||
793 | |||
794 | bool sat = i->saturate; |
||
795 | bool abs = i->src(0).mod.abs(); |
||
796 | bool neg = i->src(0).mod.neg(); |
||
797 | |||
798 | RoundMode rnd = i->rnd; |
||
799 | |||
800 | switch (i->op) { |
||
801 | case OP_CEIL: rnd = f2f ? ROUND_PI : ROUND_P; break; |
||
802 | case OP_FLOOR: rnd = f2f ? ROUND_MI : ROUND_M; break; |
||
803 | case OP_TRUNC: rnd = f2f ? ROUND_ZI : ROUND_Z; break; |
||
804 | case OP_SAT: sat = true; break; |
||
805 | case OP_NEG: neg = !neg; break; |
||
806 | case OP_ABS: abs = true; neg = false; break; |
||
807 | default: |
||
808 | break; |
||
809 | } |
||
810 | |||
811 | uint32_t op; |
||
812 | |||
813 | if (f2f) op = 0x254; |
||
814 | else if (f2i) op = 0x258; |
||
815 | else if (i2f) op = 0x25c; |
||
816 | else op = 0x260; |
||
817 | |||
818 | emitForm_C(i, op, 0x2); |
||
819 | |||
820 | FTZ_(2f); |
||
821 | if (neg) code[1] |= 1 << 16; |
||
822 | if (abs) code[1] |= 1 << 20; |
||
823 | if (sat) code[1] |= 1 << 21; |
||
824 | |||
825 | emitRoundMode(rnd, 32 + 10, f2f ? (32 + 13) : -1); |
||
826 | |||
827 | code[0] |= typeSizeofLog2(i->dType) << 10; |
||
828 | code[0] |= typeSizeofLog2(i->sType) << 12; |
||
829 | |||
830 | if (isSignedIntType(i->dType)) |
||
831 | code[0] |= 0x4000; |
||
832 | if (isSignedIntType(i->sType)) |
||
833 | code[0] |= 0x8000; |
||
834 | } |
||
835 | |||
836 | void |
||
837 | CodeEmitterGK110::emitSET(const CmpInstruction *i) |
||
838 | { |
||
839 | uint16_t op1, op2; |
||
840 | |||
841 | if (i->def(0).getFile() == FILE_PREDICATE) { |
||
842 | switch (i->sType) { |
||
843 | case TYPE_F32: op2 = 0x1d8; op1 = 0xb58; break; |
||
844 | case TYPE_F64: op2 = 0x1c0; op1 = 0xb40; break; |
||
845 | default: |
||
846 | op2 = 0x1b0; |
||
847 | op1 = 0xb30; |
||
848 | break; |
||
849 | } |
||
850 | emitForm_21(i, op2, op1); |
||
851 | |||
852 | NEG_(2e, 0); |
||
853 | ABS_(9, 0); |
||
854 | if (!(code[0] & 0x1)) { |
||
855 | NEG_(8, 1); |
||
856 | ABS_(2f, 1); |
||
857 | } else { |
||
858 | modNegAbsF32_3b(i, 1); |
||
859 | } |
||
860 | FTZ_(32); |
||
861 | |||
862 | // normal DST field is negated predicate result |
||
863 | code[0] = (code[0] & ~0xfc) | ((code[0] << 3) & 0xe0); |
||
864 | if (i->defExists(1)) |
||
865 | defId(i->def(1), 2); |
||
866 | else |
||
867 | code[0] |= 0x1c; |
||
868 | } else { |
||
869 | switch (i->sType) { |
||
870 | case TYPE_F32: op2 = 0x000; op1 = 0x820; break; |
||
871 | case TYPE_F64: op2 = 0x080; op1 = 0x900; break; |
||
872 | default: |
||
873 | op2 = 0x1a8; |
||
874 | op1 = 0xb28; |
||
875 | break; |
||
876 | } |
||
877 | emitForm_21(i, op2, op1); |
||
878 | |||
879 | NEG_(2e, 0); |
||
880 | ABS_(39, 0); |
||
881 | if (!(code[0] & 0x1)) { |
||
882 | NEG_(38, 1); |
||
883 | ABS_(2f, 1); |
||
884 | } else { |
||
885 | modNegAbsF32_3b(i, 1); |
||
886 | } |
||
887 | FTZ_(3a); |
||
888 | } |
||
889 | if (i->sType == TYPE_S32) |
||
890 | code[1] |= 1 << 19; |
||
891 | |||
892 | if (i->op != OP_SET) { |
||
893 | switch (i->op) { |
||
894 | case OP_SET_AND: code[1] |= 0x0 << 16; break; |
||
895 | case OP_SET_OR: code[1] |= 0x1 << 16; break; |
||
896 | case OP_SET_XOR: code[1] |= 0x2 << 16; break; |
||
897 | default: |
||
898 | assert(0); |
||
899 | break; |
||
900 | } |
||
901 | srcId(i->src(2), 0x2a); |
||
902 | } else { |
||
903 | code[1] |= 0x7 << 10; |
||
904 | } |
||
905 | emitCondCode(i->setCond, |
||
906 | isFloatType(i->sType) ? 0x33 : 0x34, |
||
907 | isFloatType(i->sType) ? 0xf : 0x7); |
||
908 | } |
||
909 | |||
910 | void |
||
911 | CodeEmitterGK110::emitSLCT(const CmpInstruction *i) |
||
912 | { |
||
913 | CondCode cc = i->setCond; |
||
914 | if (i->src(2).mod.neg()) |
||
915 | cc = reverseCondCode(cc); |
||
916 | |||
917 | if (i->dType == TYPE_F32) { |
||
918 | emitForm_21(i, 0x1d0, 0xb50); |
||
919 | FTZ_(32); |
||
920 | emitCondCode(cc, 0x33, 0xf); |
||
921 | } else { |
||
922 | emitForm_21(i, 0x1a4, 0xb20); |
||
923 | emitCondCode(cc, 0x34, 0x7); |
||
924 | } |
||
925 | } |
||
926 | |||
927 | void CodeEmitterGK110::emitSELP(const Instruction *i) |
||
928 | { |
||
929 | emitForm_21(i, 0x250, 0x050); |
||
930 | |||
931 | if ((i->cc == CC_NOT_P) ^ (bool)(i->src(2).mod & Modifier(NV50_IR_MOD_NOT))) |
||
932 | code[1] |= 1 << 13; |
||
933 | } |
||
934 | |||
935 | void CodeEmitterGK110::emitTEXBAR(const Instruction *i) |
||
936 | { |
||
937 | code[0] = 0x00000002 | (i->subOp << 23); |
||
938 | code[1] = 0x77000000; |
||
939 | |||
940 | emitPredicate(i); |
||
941 | } |
||
942 | |||
943 | void CodeEmitterGK110::emitTEXCSAA(const TexInstruction *i) |
||
944 | { |
||
945 | emitNOP(i); // TODO |
||
946 | } |
||
947 | |||
948 | static inline bool |
||
949 | isNextIndependentTex(const TexInstruction *i) |
||
950 | { |
||
951 | if (!i->next || !isTextureOp(i->next->op)) |
||
952 | return false; |
||
953 | if (i->getDef(0)->interfers(i->next->getSrc(0))) |
||
954 | return false; |
||
955 | return !i->next->srcExists(1) || !i->getDef(0)->interfers(i->next->getSrc(1)); |
||
956 | } |
||
957 | |||
958 | void |
||
959 | CodeEmitterGK110::emitTEX(const TexInstruction *i) |
||
960 | { |
||
961 | const bool ind = i->tex.rIndirectSrc >= 0; |
||
962 | |||
963 | if (ind) { |
||
964 | code[0] = 0x00000002; |
||
965 | switch (i->op) { |
||
966 | case OP_TXD: |
||
967 | code[1] = 0x7e000000; |
||
968 | break; |
||
969 | default: |
||
970 | code[1] = 0x7d800000; |
||
971 | break; |
||
972 | } |
||
973 | } else { |
||
974 | switch (i->op) { |
||
975 | case OP_TXD: |
||
976 | code[0] = 0x00000002; |
||
977 | code[1] = 0x76000000; |
||
978 | break; |
||
979 | default: |
||
980 | code[0] = 0x00000001; |
||
981 | code[1] = 0x60000000; |
||
982 | break; |
||
983 | } |
||
984 | code[1] |= i->tex.r << 15; |
||
985 | } |
||
986 | |||
987 | code[1] |= isNextIndependentTex(i) ? 0x1 : 0x2; // t : p mode |
||
988 | |||
989 | // if (i->tex.liveOnly) |
||
990 | // ? |
||
991 | |||
992 | switch (i->op) { |
||
993 | case OP_TEX: break; |
||
994 | case OP_TXB: code[1] |= 0x2000; break; |
||
995 | case OP_TXL: code[1] |= 0x3000; break; |
||
996 | case OP_TXF: break; // XXX |
||
997 | case OP_TXG: break; // XXX |
||
998 | case OP_TXD: break; |
||
999 | default: |
||
1000 | assert(!"invalid texture op"); |
||
1001 | break; |
||
1002 | } |
||
1003 | /* |
||
1004 | if (i->op == OP_TXF) { |
||
1005 | if (!i->tex.levelZero) |
||
1006 | code[1] |= 0x02000000; |
||
1007 | } else */ |
||
1008 | if (i->tex.levelZero) { |
||
1009 | code[1] |= 0x1000; |
||
1010 | } |
||
1011 | |||
1012 | // if (i->op != OP_TXD && i->tex.derivAll) |
||
1013 | // code[1] |= 1 << 13; |
||
1014 | |||
1015 | emitPredicate(i); |
||
1016 | |||
1017 | code[1] |= i->tex.mask << 2; |
||
1018 | |||
1019 | const int src1 = (i->predSrc == 1) ? 2 : 1; // if predSrc == 1, !srcExists(2) |
||
1020 | |||
1021 | defId(i->def(0), 2); |
||
1022 | srcId(i->src(0), 10); |
||
1023 | srcId(i, src1, 23); |
||
1024 | |||
1025 | // if (i->op == OP_TXG) code[0] |= i->tex.gatherComp << 5; |
||
1026 | |||
1027 | // texture target: |
||
1028 | code[1] |= (i->tex.target.isCube() ? 3 : (i->tex.target.getDim() - 1)) << 7; |
||
1029 | if (i->tex.target.isArray()) |
||
1030 | code[1] |= 0x40; |
||
1031 | // if (i->tex.target.isShadow()) |
||
1032 | // ? |
||
1033 | // if (i->tex.target == TEX_TARGET_2D_MS || |
||
1034 | // i->tex.target == TEX_TARGET_2D_MS_ARRAY) |
||
1035 | // ? |
||
1036 | |||
1037 | if (i->srcExists(src1) && i->src(src1).getFile() == FILE_IMMEDIATE) { |
||
1038 | // ? |
||
1039 | } |
||
1040 | |||
1041 | // if (i->tex.useOffsets) |
||
1042 | // ? |
||
1043 | } |
||
1044 | |||
1045 | void |
||
1046 | CodeEmitterGK110::emitTXQ(const TexInstruction *i) |
||
1047 | { |
||
1048 | emitNOP(i); // TODO |
||
1049 | } |
||
1050 | |||
1051 | void |
||
1052 | CodeEmitterGK110::emitQUADOP(const Instruction *i, uint8_t qOp, uint8_t laneMask) |
||
1053 | { |
||
1054 | emitNOP(i); // TODO |
||
1055 | } |
||
1056 | |||
1057 | void |
||
1058 | CodeEmitterGK110::emitFlow(const Instruction *i) |
||
1059 | { |
||
1060 | const FlowInstruction *f = i->asFlow(); |
||
1061 | |||
1062 | unsigned mask; // bit 0: predicate, bit 1: target |
||
1063 | |||
1064 | code[0] = 0x00000000; |
||
1065 | |||
1066 | switch (i->op) { |
||
1067 | case OP_BRA: |
||
1068 | code[1] = f->absolute ? 0x00000 : 0x12000000; // XXX |
||
1069 | // if (i->srcExists(0) && i->src(0).getFile() == FILE_MEMORY_CONST) |
||
1070 | // code[0] |= 0x4000; |
||
1071 | mask = 3; |
||
1072 | break; |
||
1073 | case OP_CALL: |
||
1074 | code[1] = f->absolute ? 0x00000 : 0x13000000; // XXX |
||
1075 | // if (i->srcExists(0) && i->src(0).getFile() == FILE_MEMORY_CONST) |
||
1076 | // code[0] |= 0x4000; |
||
1077 | mask = 2; |
||
1078 | break; |
||
1079 | |||
1080 | case OP_EXIT: code[1] = 0x18000000; mask = 1; break; |
||
1081 | case OP_RET: code[1] = 0x19000000; mask = 1; break; |
||
1082 | case OP_DISCARD: code[1] = 0x19800000; mask = 1; break; // XXX: guess |
||
1083 | case OP_BREAK: code[1] = 0x1a800000; mask = 1; break; // XXX: guess |
||
1084 | case OP_CONT: code[1] = 0x1b000000; mask = 1; break; // XXX: guess |
||
1085 | |||
1086 | case OP_JOINAT: code[1] = 0x14800000; mask = 2; break; |
||
1087 | case OP_PREBREAK: code[1] = 0x15000000; mask = 2; break; // XXX: guess |
||
1088 | case OP_PRECONT: code[1] = 0x15800000; mask = 2; break; // XXX: guess |
||
1089 | case OP_PRERET: code[1] = 0x16000000; mask = 2; break; // XXX: guess |
||
1090 | |||
1091 | case OP_QUADON: code[1] = 0x1c000000; mask = 0; break; // XXX: guess |
||
1092 | case OP_QUADPOP: code[1] = 0x1c800000; mask = 0; break; // XXX: guess |
||
1093 | case OP_BRKPT: code[1] = 0x1d000000; mask = 0; break; // XXX: guess |
||
1094 | default: |
||
1095 | assert(!"invalid flow operation"); |
||
1096 | return; |
||
1097 | } |
||
1098 | |||
1099 | if (mask & 1) { |
||
1100 | emitPredicate(i); |
||
1101 | if (i->flagsSrc < 0) |
||
1102 | code[0] |= 0x3c; |
||
1103 | } |
||
1104 | |||
1105 | if (!f) |
||
1106 | return; |
||
1107 | |||
1108 | // TODO |
||
1109 | /* |
||
1110 | if (f->allWarp) |
||
1111 | code[0] |= 1 << 15; |
||
1112 | if (f->limit) |
||
1113 | code[0] |= 1 << 16; |
||
1114 | */ |
||
1115 | |||
1116 | if (f->op == OP_CALL) { |
||
1117 | if (f->builtin) { |
||
1118 | assert(f->absolute); |
||
1119 | uint32_t pcAbs = targNVC0->getBuiltinOffset(f->target.builtin); |
||
1120 | addReloc(RelocEntry::TYPE_BUILTIN, 0, pcAbs, 0xff800000, 23); |
||
1121 | addReloc(RelocEntry::TYPE_BUILTIN, 1, pcAbs, 0x007fffff, -9); |
||
1122 | } else { |
||
1123 | assert(!f->absolute); |
||
1124 | int32_t pcRel = f->target.fn->binPos - (codeSize + 8); |
||
1125 | code[0] |= (pcRel & 0x1ff) << 23; |
||
1126 | code[1] |= (pcRel >> 9) & 0x7fff; |
||
1127 | } |
||
1128 | } else |
||
1129 | if (mask & 2) { |
||
1130 | int32_t pcRel = f->target.bb->binPos - (codeSize + 8); |
||
1131 | // currently we don't want absolute branches |
||
1132 | assert(!f->absolute); |
||
1133 | code[0] |= (pcRel & 0x1ff) << 23; |
||
1134 | code[1] |= (pcRel >> 9) & 0x7fff; |
||
1135 | } |
||
1136 | } |
||
1137 | |||
1138 | void |
||
1139 | CodeEmitterGK110::emitPFETCH(const Instruction *i) |
||
1140 | { |
||
1141 | emitNOP(i); // TODO |
||
1142 | } |
||
1143 | |||
1144 | void |
||
1145 | CodeEmitterGK110::emitVFETCH(const Instruction *i) |
||
1146 | { |
||
1147 | uint32_t offset = i->src(0).get()->reg.data.offset; |
||
1148 | |||
1149 | code[0] = 0x00000002 | (offset << 23); |
||
1150 | code[1] = 0x7ec00000 | (offset >> 9); |
||
1151 | |||
1152 | #if 0 |
||
1153 | if (i->perPatch) |
||
1154 | code[0] |= 0x100; |
||
1155 | if (i->getSrc(0)->reg.file == FILE_SHADER_OUTPUT) |
||
1156 | code[0] |= 0x200; // yes, TCPs can read from *outputs* of other threads |
||
1157 | #endif |
||
1158 | |||
1159 | emitPredicate(i); |
||
1160 | |||
1161 | defId(i->def(0), 2); |
||
1162 | srcId(i->src(0).getIndirect(0), 10); |
||
1163 | srcId(i->src(0).getIndirect(1), 32 + 10); // vertex address |
||
1164 | } |
||
1165 | |||
1166 | void |
||
1167 | CodeEmitterGK110::emitEXPORT(const Instruction *i) |
||
1168 | { |
||
1169 | uint32_t offset = i->src(0).get()->reg.data.offset; |
||
1170 | |||
1171 | code[0] = 0x00000002 | (offset << 23); |
||
1172 | code[1] = 0x7f000000 | (offset >> 9); |
||
1173 | |||
1174 | #if 0 |
||
1175 | if (i->perPatch) |
||
1176 | code[0] |= 0x100; |
||
1177 | #endif |
||
1178 | |||
1179 | emitPredicate(i); |
||
1180 | |||
1181 | assert(i->src(1).getFile() == FILE_GPR); |
||
1182 | |||
1183 | srcId(i->src(0).getIndirect(0), 10); |
||
1184 | srcId(i->src(0).getIndirect(1), 32 + 10); // vertex base address |
||
1185 | srcId(i->src(1), 2); |
||
1186 | } |
||
1187 | |||
1188 | void |
||
1189 | CodeEmitterGK110::emitOUT(const Instruction *i) |
||
1190 | { |
||
1191 | emitNOP(i); // TODO |
||
1192 | } |
||
1193 | |||
1194 | void |
||
1195 | CodeEmitterGK110::emitInterpMode(const Instruction *i) |
||
1196 | { |
||
1197 | code[1] |= i->ipa << 21; // TODO: INTERP_SAMPLEID |
||
1198 | } |
||
1199 | |||
1200 | void |
||
1201 | CodeEmitterGK110::emitINTERP(const Instruction *i) |
||
1202 | { |
||
1203 | const uint32_t base = i->getSrc(0)->reg.data.offset; |
||
1204 | |||
1205 | code[0] = 0x00000002 | (base << 31); |
||
1206 | code[1] = 0x74800000 | (base >> 1); |
||
1207 | |||
1208 | if (i->saturate) |
||
1209 | code[1] |= 1 << 18; |
||
1210 | |||
1211 | if (i->op == OP_PINTERP) |
||
1212 | srcId(i->src(1), 23); |
||
1213 | else |
||
1214 | code[0] |= 0xff << 23; |
||
1215 | |||
1216 | srcId(i->src(0).getIndirect(0), 10); |
||
1217 | emitInterpMode(i); |
||
1218 | |||
1219 | emitPredicate(i); |
||
1220 | defId(i->def(0), 2); |
||
1221 | |||
1222 | if (i->getSampleMode() == NV50_IR_INTERP_OFFSET) |
||
1223 | srcId(i->src(i->op == OP_PINTERP ? 2 : 1), 32 + 10); |
||
1224 | else |
||
1225 | code[1] |= 0xff << 10; |
||
1226 | } |
||
1227 | |||
1228 | void |
||
1229 | CodeEmitterGK110::emitLoadStoreType(DataType ty, const int pos) |
||
1230 | { |
||
1231 | uint8_t n; |
||
1232 | |||
1233 | switch (ty) { |
||
1234 | case TYPE_U8: |
||
1235 | n = 0; |
||
1236 | break; |
||
1237 | case TYPE_S8: |
||
1238 | n = 1; |
||
1239 | break; |
||
1240 | case TYPE_U16: |
||
1241 | n = 2; |
||
1242 | break; |
||
1243 | case TYPE_S16: |
||
1244 | n = 3; |
||
1245 | break; |
||
1246 | case TYPE_F32: |
||
1247 | case TYPE_U32: |
||
1248 | case TYPE_S32: |
||
1249 | n = 4; |
||
1250 | break; |
||
1251 | case TYPE_F64: |
||
1252 | case TYPE_U64: |
||
1253 | case TYPE_S64: |
||
1254 | n = 5; |
||
1255 | break; |
||
1256 | case TYPE_B128: |
||
1257 | n = 6; |
||
1258 | break; |
||
1259 | default: |
||
1260 | n = 0; |
||
1261 | assert(!"invalid ld/st type"); |
||
1262 | break; |
||
1263 | } |
||
1264 | code[pos / 32] |= n << (pos % 32); |
||
1265 | } |
||
1266 | |||
1267 | void |
||
1268 | CodeEmitterGK110::emitCachingMode(CacheMode c, const int pos) |
||
1269 | { |
||
1270 | uint8_t n; |
||
1271 | |||
1272 | switch (c) { |
||
1273 | case CACHE_CA: |
||
1274 | // case CACHE_WB: |
||
1275 | n = 0; |
||
1276 | break; |
||
1277 | case CACHE_CG: |
||
1278 | n = 1; |
||
1279 | break; |
||
1280 | case CACHE_CS: |
||
1281 | n = 2; |
||
1282 | break; |
||
1283 | case CACHE_CV: |
||
1284 | // case CACHE_WT: |
||
1285 | n = 3; |
||
1286 | break; |
||
1287 | default: |
||
1288 | n = 0; |
||
1289 | assert(!"invalid caching mode"); |
||
1290 | break; |
||
1291 | } |
||
1292 | code[pos / 32] |= n << (pos % 32); |
||
1293 | } |
||
1294 | |||
1295 | void |
||
1296 | CodeEmitterGK110::emitSTORE(const Instruction *i) |
||
1297 | { |
||
1298 | int32_t offset = SDATA(i->src(0)).offset; |
||
1299 | |||
1300 | switch (i->src(0).getFile()) { |
||
1301 | case FILE_MEMORY_GLOBAL: code[1] = 0xe0000000; code[0] = 0x00000000; break; |
||
1302 | case FILE_MEMORY_LOCAL: code[1] = 0x7a800000; code[0] = 0x00000002; break; |
||
1303 | case FILE_MEMORY_SHARED: code[1] = 0x7ac00000; code[0] = 0x00000002; break; |
||
1304 | default: |
||
1305 | assert(!"invalid memory file"); |
||
1306 | break; |
||
1307 | } |
||
1308 | |||
1309 | if (i->src(0).getFile() != FILE_MEMORY_GLOBAL) |
||
1310 | offset &= 0xffffff; |
||
1311 | |||
1312 | if (code[0] & 0x2) { |
||
1313 | emitLoadStoreType(i->dType, 0x33); |
||
1314 | if (i->src(0).getFile() == FILE_MEMORY_LOCAL) |
||
1315 | emitCachingMode(i->cache, 0x2f); |
||
1316 | } else { |
||
1317 | emitLoadStoreType(i->dType, 0x38); |
||
1318 | emitCachingMode(i->cache, 0x3b); |
||
1319 | } |
||
1320 | code[0] |= offset << 23; |
||
1321 | code[1] |= offset >> 9; |
||
1322 | |||
1323 | emitPredicate(i); |
||
1324 | |||
1325 | srcId(i->src(1), 2); |
||
1326 | srcId(i->src(0).getIndirect(0), 10); |
||
1327 | } |
||
1328 | |||
1329 | void |
||
1330 | CodeEmitterGK110::emitLOAD(const Instruction *i) |
||
1331 | { |
||
1332 | int32_t offset = SDATA(i->src(0)).offset; |
||
1333 | |||
1334 | switch (i->src(0).getFile()) { |
||
1335 | case FILE_MEMORY_GLOBAL: code[1] = 0xc0000000; code[0] = 0x00000000; break; |
||
1336 | case FILE_MEMORY_LOCAL: code[1] = 0x7a000000; code[0] = 0x00000002; break; |
||
1337 | case FILE_MEMORY_SHARED: code[1] = 0x7ac00000; code[0] = 0x00000002; break; |
||
1338 | case FILE_MEMORY_CONST: |
||
1339 | if (!i->src(0).isIndirect(0) && typeSizeof(i->dType) == 4) { |
||
1340 | emitMOV(i); |
||
1341 | return; |
||
1342 | } |
||
1343 | offset &= 0xffff; |
||
1344 | code[0] = 0x00000002; |
||
1345 | code[1] = 0x7c800000 | (i->src(0).get()->reg.fileIndex << 7); |
||
1346 | break; |
||
1347 | default: |
||
1348 | assert(!"invalid memory file"); |
||
1349 | break; |
||
1350 | } |
||
1351 | |||
1352 | if (code[0] & 0x2) { |
||
1353 | offset &= 0xffffff; |
||
1354 | emitLoadStoreType(i->dType, 0x33); |
||
1355 | if (i->src(0).getFile() == FILE_MEMORY_LOCAL) |
||
1356 | emitCachingMode(i->cache, 0x2f); |
||
1357 | } else { |
||
1358 | emitLoadStoreType(i->dType, 0x38); |
||
1359 | emitCachingMode(i->cache, 0x3b); |
||
1360 | } |
||
1361 | code[0] |= offset << 23; |
||
1362 | code[1] |= offset >> 9; |
||
1363 | |||
1364 | emitPredicate(i); |
||
1365 | |||
1366 | defId(i->def(0), 2); |
||
1367 | srcId(i->src(0).getIndirect(0), 10); |
||
1368 | } |
||
1369 | |||
1370 | uint8_t |
||
1371 | CodeEmitterGK110::getSRegEncoding(const ValueRef& ref) |
||
1372 | { |
||
1373 | switch (SDATA(ref).sv.sv) { |
||
1374 | case SV_LANEID: return 0x00; |
||
1375 | case SV_PHYSID: return 0x03; |
||
1376 | case SV_VERTEX_COUNT: return 0x10; |
||
1377 | case SV_INVOCATION_ID: return 0x11; |
||
1378 | case SV_YDIR: return 0x12; |
||
1379 | case SV_TID: return 0x21 + SDATA(ref).sv.index; |
||
1380 | case SV_CTAID: return 0x25 + SDATA(ref).sv.index; |
||
1381 | case SV_NTID: return 0x29 + SDATA(ref).sv.index; |
||
1382 | case SV_GRIDID: return 0x2c; |
||
1383 | case SV_NCTAID: return 0x2d + SDATA(ref).sv.index; |
||
1384 | case SV_LBASE: return 0x34; |
||
1385 | case SV_SBASE: return 0x30; |
||
1386 | case SV_CLOCK: return 0x50 + SDATA(ref).sv.index; |
||
1387 | default: |
||
1388 | assert(!"no sreg for system value"); |
||
1389 | return 0; |
||
1390 | } |
||
1391 | } |
||
1392 | |||
1393 | void |
||
1394 | CodeEmitterGK110::emitMOV(const Instruction *i) |
||
1395 | { |
||
1396 | if (i->src(0).getFile() == FILE_SYSTEM_VALUE) { |
||
1397 | code[0] = 0x00000002 | (getSRegEncoding(i->src(0)) << 23); |
||
1398 | code[1] = 0x86400000; |
||
1399 | emitPredicate(i); |
||
1400 | defId(i->def(0), 2); |
||
1401 | } else |
||
1402 | if (i->src(0).getFile() == FILE_IMMEDIATE) { |
||
1403 | code[0] = 0x00000002 | (i->lanes << 14); |
||
1404 | code[1] = 0x74000000; |
||
1405 | emitPredicate(i); |
||
1406 | defId(i->def(0), 2); |
||
1407 | setImmediate32(i, 0, Modifier(0)); |
||
1408 | } else |
||
1409 | if (i->src(0).getFile() == FILE_PREDICATE) { |
||
1410 | // TODO |
||
1411 | } else { |
||
1412 | emitForm_C(i, 0x24c, 2); |
||
1413 | code[1] |= i->lanes << 10; |
||
1414 | } |
||
1415 | } |
||
1416 | |||
1417 | bool |
||
1418 | CodeEmitterGK110::emitInstruction(Instruction *insn) |
||
1419 | { |
||
1420 | const unsigned int size = (writeIssueDelays && !(codeSize & 0x3f)) ? 16 : 8; |
||
1421 | |||
1422 | if (insn->encSize != 8) { |
||
1423 | ERROR("skipping unencodable instruction: "); |
||
1424 | insn->print(); |
||
1425 | return false; |
||
1426 | } else |
||
1427 | if (codeSize + size > codeSizeLimit) { |
||
1428 | ERROR("code emitter output buffer too small\n"); |
||
1429 | return false; |
||
1430 | } |
||
1431 | |||
1432 | if (writeIssueDelays) { |
||
1433 | int id = (codeSize & 0x3f) / 8 - 1; |
||
1434 | if (id < 0) { |
||
1435 | id += 1; |
||
1436 | code[0] = 0x00000000; // cf issue delay "instruction" |
||
1437 | code[1] = 0x08000000; |
||
1438 | code += 2; |
||
1439 | codeSize += 8; |
||
1440 | } |
||
1441 | uint32_t *data = code - (id * 2 + 2); |
||
1442 | |||
1443 | switch (id) { |
||
1444 | case 0: data[0] |= insn->sched << 2; break; |
||
1445 | case 1: data[0] |= insn->sched << 10; break; |
||
1446 | case 2: data[0] |= insn->sched << 18; break; |
||
1447 | case 3: data[0] |= insn->sched << 26; data[1] |= insn->sched >> 6; break; |
||
1448 | case 4: data[1] |= insn->sched << 2; |
||
1449 | case 5: data[1] |= insn->sched << 10; break; |
||
1450 | case 6: data[1] |= insn->sched << 18; break; |
||
1451 | default: |
||
1452 | assert(0); |
||
1453 | break; |
||
1454 | } |
||
1455 | } |
||
1456 | |||
1457 | // assert that instructions with multiple defs don't corrupt registers |
||
1458 | for (int d = 0; insn->defExists(d); ++d) |
||
1459 | assert(insn->asTex() || insn->def(d).rep()->reg.data.id >= 0); |
||
1460 | |||
1461 | switch (insn->op) { |
||
1462 | case OP_MOV: |
||
1463 | case OP_RDSV: |
||
1464 | emitMOV(insn); |
||
1465 | break; |
||
1466 | case OP_NOP: |
||
1467 | break; |
||
1468 | case OP_LOAD: |
||
1469 | emitLOAD(insn); |
||
1470 | break; |
||
1471 | case OP_STORE: |
||
1472 | emitSTORE(insn); |
||
1473 | break; |
||
1474 | case OP_LINTERP: |
||
1475 | case OP_PINTERP: |
||
1476 | emitINTERP(insn); |
||
1477 | break; |
||
1478 | case OP_VFETCH: |
||
1479 | emitVFETCH(insn); |
||
1480 | break; |
||
1481 | case OP_EXPORT: |
||
1482 | emitEXPORT(insn); |
||
1483 | break; |
||
1484 | case OP_PFETCH: |
||
1485 | emitPFETCH(insn); |
||
1486 | break; |
||
1487 | case OP_EMIT: |
||
1488 | case OP_RESTART: |
||
1489 | emitOUT(insn); |
||
1490 | break; |
||
1491 | case OP_ADD: |
||
1492 | case OP_SUB: |
||
1493 | if (isFloatType(insn->dType)) |
||
1494 | emitFADD(insn); |
||
1495 | else |
||
1496 | emitUADD(insn); |
||
1497 | break; |
||
1498 | case OP_MUL: |
||
1499 | if (isFloatType(insn->dType)) |
||
1500 | emitFMUL(insn); |
||
1501 | else |
||
1502 | emitIMUL(insn); |
||
1503 | break; |
||
1504 | case OP_MAD: |
||
1505 | case OP_FMA: |
||
1506 | if (isFloatType(insn->dType)) |
||
1507 | emitFMAD(insn); |
||
1508 | else |
||
1509 | emitIMAD(insn); |
||
1510 | break; |
||
1511 | case OP_SAD: |
||
1512 | emitISAD(insn); |
||
1513 | break; |
||
1514 | case OP_NOT: |
||
1515 | emitNOT(insn); |
||
1516 | break; |
||
1517 | case OP_AND: |
||
1518 | emitLogicOp(insn, 0); |
||
1519 | break; |
||
1520 | case OP_OR: |
||
1521 | emitLogicOp(insn, 1); |
||
1522 | break; |
||
1523 | case OP_XOR: |
||
1524 | emitLogicOp(insn, 2); |
||
1525 | break; |
||
1526 | case OP_SHL: |
||
1527 | case OP_SHR: |
||
1528 | emitShift(insn); |
||
1529 | break; |
||
1530 | case OP_SET: |
||
1531 | case OP_SET_AND: |
||
1532 | case OP_SET_OR: |
||
1533 | case OP_SET_XOR: |
||
1534 | emitSET(insn->asCmp()); |
||
1535 | break; |
||
1536 | case OP_SELP: |
||
1537 | emitSELP(insn); |
||
1538 | break; |
||
1539 | case OP_SLCT: |
||
1540 | emitSLCT(insn->asCmp()); |
||
1541 | break; |
||
1542 | case OP_MIN: |
||
1543 | case OP_MAX: |
||
1544 | emitMINMAX(insn); |
||
1545 | break; |
||
1546 | case OP_ABS: |
||
1547 | case OP_NEG: |
||
1548 | case OP_CEIL: |
||
1549 | case OP_FLOOR: |
||
1550 | case OP_TRUNC: |
||
1551 | case OP_CVT: |
||
1552 | case OP_SAT: |
||
1553 | emitCVT(insn); |
||
1554 | break; |
||
1555 | case OP_RSQ: |
||
1556 | emitSFnOp(insn, 5); |
||
1557 | break; |
||
1558 | case OP_RCP: |
||
1559 | emitSFnOp(insn, 4); |
||
1560 | break; |
||
1561 | case OP_LG2: |
||
1562 | emitSFnOp(insn, 3); |
||
1563 | break; |
||
1564 | case OP_EX2: |
||
1565 | emitSFnOp(insn, 2); |
||
1566 | break; |
||
1567 | case OP_SIN: |
||
1568 | emitSFnOp(insn, 1); |
||
1569 | break; |
||
1570 | case OP_COS: |
||
1571 | emitSFnOp(insn, 0); |
||
1572 | break; |
||
1573 | case OP_PRESIN: |
||
1574 | case OP_PREEX2: |
||
1575 | emitPreOp(insn); |
||
1576 | break; |
||
1577 | case OP_TEX: |
||
1578 | case OP_TXB: |
||
1579 | case OP_TXL: |
||
1580 | case OP_TXD: |
||
1581 | case OP_TXF: |
||
1582 | emitTEX(insn->asTex()); |
||
1583 | break; |
||
1584 | case OP_TXQ: |
||
1585 | emitTXQ(insn->asTex()); |
||
1586 | break; |
||
1587 | case OP_TEXBAR: |
||
1588 | emitTEXBAR(insn); |
||
1589 | break; |
||
1590 | case OP_BRA: |
||
1591 | case OP_CALL: |
||
1592 | case OP_PRERET: |
||
1593 | case OP_RET: |
||
1594 | case OP_DISCARD: |
||
1595 | case OP_EXIT: |
||
1596 | case OP_PRECONT: |
||
1597 | case OP_CONT: |
||
1598 | case OP_PREBREAK: |
||
1599 | case OP_BREAK: |
||
1600 | case OP_JOINAT: |
||
1601 | case OP_BRKPT: |
||
1602 | case OP_QUADON: |
||
1603 | case OP_QUADPOP: |
||
1604 | emitFlow(insn); |
||
1605 | break; |
||
1606 | case OP_QUADOP: |
||
1607 | emitQUADOP(insn, insn->subOp, insn->lanes); |
||
1608 | break; |
||
1609 | case OP_DFDX: |
||
1610 | emitQUADOP(insn, insn->src(0).mod.neg() ? 0x66 : 0x99, 0x4); |
||
1611 | break; |
||
1612 | case OP_DFDY: |
||
1613 | emitQUADOP(insn, insn->src(0).mod.neg() ? 0x5a : 0xa5, 0x5); |
||
1614 | break; |
||
1615 | case OP_POPCNT: |
||
1616 | emitPOPC(insn); |
||
1617 | break; |
||
1618 | case OP_JOIN: |
||
1619 | emitNOP(insn); |
||
1620 | insn->join = 1; |
||
1621 | break; |
||
1622 | case OP_PHI: |
||
1623 | case OP_UNION: |
||
1624 | case OP_CONSTRAINT: |
||
1625 | ERROR("operation should have been eliminated"); |
||
1626 | return false; |
||
1627 | case OP_EXP: |
||
1628 | case OP_LOG: |
||
1629 | case OP_SQRT: |
||
1630 | case OP_POW: |
||
1631 | ERROR("operation should have been lowered\n"); |
||
1632 | return false; |
||
1633 | default: |
||
1634 | ERROR("unknow op\n"); |
||
1635 | return false; |
||
1636 | } |
||
1637 | |||
1638 | if (insn->join) |
||
1639 | code[0] |= 1 << 22; |
||
1640 | |||
1641 | code += 2; |
||
1642 | codeSize += 8; |
||
1643 | return true; |
||
1644 | } |
||
1645 | |||
1646 | uint32_t |
||
1647 | CodeEmitterGK110::getMinEncodingSize(const Instruction *i) const |
||
1648 | { |
||
1649 | // No more short instruction encodings. |
||
1650 | return 8; |
||
1651 | } |
||
1652 | |||
1653 | void |
||
1654 | CodeEmitterGK110::prepareEmission(Function *func) |
||
1655 | { |
||
1656 | const Target *targ = func->getProgram()->getTarget(); |
||
1657 | |||
1658 | CodeEmitter::prepareEmission(func); |
||
1659 | |||
1660 | if (targ->hasSWSched) |
||
1661 | calculateSchedDataNVC0(targ, func); |
||
1662 | } |
||
1663 | |||
1664 | CodeEmitterGK110::CodeEmitterGK110(const TargetNVC0 *target) |
||
1665 | : CodeEmitter(target), |
||
1666 | targNVC0(target), |
||
1667 | writeIssueDelays(target->hasSWSched) |
||
1668 | { |
||
1669 | code = NULL; |
||
1670 | codeSize = codeSizeLimit = 0; |
||
1671 | relocInfo = NULL; |
||
1672 | } |
||
1673 | |||
1674 | CodeEmitter * |
||
1675 | TargetNVC0::createCodeEmitterGK110(Program::Type type) |
||
1676 | { |
||
1677 | CodeEmitterGK110 *emit = new CodeEmitterGK110(this); |
||
1678 | emit->setProgramType(type); |
||
1679 | return emit; |
||
1680 | } |
||
1681 | |||
1682 | } // namespace nv50_ir><>><>><>><>><>><>><>><>>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>>><>><>><>><>><>><>><>><>=>><>><>><>><>><>><>><>><>>><>><>><>><>><>><>>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><> |