Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
5564 serge 1
/*
2
 * Copyright 2011 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 "codegen/nv50_ir_target_nv50.h"
24
 
25
namespace nv50_ir {
26
 
27
Target *getTargetNV50(unsigned int chipset)
28
{
29
   return new TargetNV50(chipset);
30
}
31
 
32
TargetNV50::TargetNV50(unsigned int card) : Target(true, true, false)
33
{
34
   chipset = card;
35
 
36
   wposMask = 0;
37
   for (unsigned int i = 0; i <= SV_LAST; ++i)
38
      sysvalLocation[i] = ~0;
39
 
40
   initOpInfo();
41
}
42
 
43
#if 0
44
// BULTINS / LIBRARY FUNCTIONS:
45
 
46
// TODO
47
static const uint32_t nvc0_builtin_code[] =
48
{
49
};
50
 
51
static const uint16_t nvc0_builtin_offsets[NV50_BUILTIN_COUNT] =
52
{
53
};
54
#endif
55
 
56
void
57
TargetNV50::getBuiltinCode(const uint32_t **code, uint32_t *size) const
58
{
59
   *code = NULL;
60
   *size = 0;
61
}
62
 
63
uint32_t
64
TargetNV50::getBuiltinOffset(int builtin) const
65
{
66
   return 0;
67
}
68
 
69
struct opProperties
70
{
71
   operation op;
72
   unsigned int mNeg    : 4;
73
   unsigned int mAbs    : 4;
74
   unsigned int mNot    : 4;
75
   unsigned int mSat    : 4;
76
   unsigned int fConst  : 3;
77
   unsigned int fShared : 3;
78
   unsigned int fAttrib : 3;
79
   unsigned int fImm    : 3;
80
};
81
 
82
static const struct opProperties _initProps[] =
83
{
84
   //           neg  abs  not  sat  c[]  s[], a[], imm
85
   { OP_ADD,    0x3, 0x0, 0x0, 0x8, 0x2, 0x1, 0x1, 0x2 },
86
   { OP_SUB,    0x3, 0x0, 0x0, 0x8, 0x2, 0x1, 0x1, 0x2 },
87
   { OP_MUL,    0x3, 0x0, 0x0, 0x0, 0x2, 0x1, 0x1, 0x2 },
88
   { OP_MAX,    0x3, 0x3, 0x0, 0x0, 0x2, 0x1, 0x1, 0x0 },
89
   { OP_MIN,    0x3, 0x3, 0x0, 0x0, 0x2, 0x1, 0x1, 0x0 },
90
   { OP_MAD,    0x7, 0x0, 0x0, 0x8, 0x6, 0x1, 0x1, 0x0 }, // special constraint
91
   { OP_ABS,    0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0 },
92
   { OP_NEG,    0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0 },
93
   { OP_CVT,    0x1, 0x1, 0x0, 0x8, 0x0, 0x1, 0x1, 0x0 },
94
   { OP_AND,    0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x2 },
95
   { OP_OR,     0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x2 },
96
   { OP_XOR,    0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x2 },
97
   { OP_SHL,    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2 },
98
   { OP_SHR,    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2 },
99
   { OP_SET,    0x3, 0x3, 0x0, 0x0, 0x2, 0x1, 0x1, 0x0 },
100
   { OP_PREEX2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
101
   { OP_PRESIN, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
102
   { OP_LG2,    0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
103
   { OP_RCP,    0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
104
   { OP_RSQ,    0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
105
   { OP_DFDX,   0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
106
   { OP_DFDY,   0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
107
};
108
 
109
void TargetNV50::initOpInfo()
110
{
111
   unsigned int i, j;
112
 
113
   static const uint32_t commutative[(OP_LAST + 31) / 32] =
114
   {
115
      // ADD,MAD,MUL,AND,OR,XOR,MAX,MIN
116
      0x0670ca00, 0x0000003f, 0x00000000, 0x00000000
117
   };
118
   static const uint32_t shortForm[(OP_LAST + 31) / 32] =
119
   {
120
      // MOV,ADD,SUB,MUL,MAD,SAD,L/PINTERP,RCP,TEX,TXF
121
      0x00014e40, 0x00000040, 0x00000498, 0x00000000
122
   };
123
   static const operation noDestList[] =
124
   {
125
      OP_STORE, OP_WRSV, OP_EXPORT, OP_BRA, OP_CALL, OP_RET, OP_EXIT,
126
      OP_DISCARD, OP_CONT, OP_BREAK, OP_PRECONT, OP_PREBREAK, OP_PRERET,
127
      OP_JOIN, OP_JOINAT, OP_BRKPT, OP_MEMBAR, OP_EMIT, OP_RESTART,
128
      OP_QUADON, OP_QUADPOP, OP_TEXBAR, OP_SUSTB, OP_SUSTP, OP_SUREDP,
129
      OP_SUREDB, OP_BAR
130
   };
131
   static const operation noPredList[] =
132
   {
133
      OP_CALL, OP_PREBREAK, OP_PRERET, OP_QUADON, OP_QUADPOP, OP_JOINAT,
134
      OP_EMIT, OP_RESTART
135
   };
136
 
137
   for (i = 0; i < DATA_FILE_COUNT; ++i)
138
      nativeFileMap[i] = (DataFile)i;
139
   nativeFileMap[FILE_PREDICATE] = FILE_FLAGS;
140
 
141
   for (i = 0; i < OP_LAST; ++i) {
142
      opInfo[i].variants = NULL;
143
      opInfo[i].op = (operation)i;
144
      opInfo[i].srcTypes = 1 << (int)TYPE_F32;
145
      opInfo[i].dstTypes = 1 << (int)TYPE_F32;
146
      opInfo[i].immdBits = 0xffffffff;
147
      opInfo[i].srcNr = operationSrcNr[i];
148
 
149
      for (j = 0; j < opInfo[i].srcNr; ++j) {
150
         opInfo[i].srcMods[j] = 0;
151
         opInfo[i].srcFiles[j] = 1 << (int)FILE_GPR;
152
      }
153
      opInfo[i].dstMods = 0;
154
      opInfo[i].dstFiles = 1 << (int)FILE_GPR;
155
 
156
      opInfo[i].hasDest = 1;
157
      opInfo[i].vector = (i >= OP_TEX && i <= OP_TEXCSAA);
158
      opInfo[i].commutative = (commutative[i / 32] >> (i % 32)) & 1;
159
      opInfo[i].pseudo = (i < OP_MOV);
160
      opInfo[i].predicate = !opInfo[i].pseudo;
161
      opInfo[i].flow = (i >= OP_BRA && i <= OP_JOIN);
162
      opInfo[i].minEncSize = (shortForm[i / 32] & (1 << (i % 32))) ? 4 : 8;
163
   }
164
   for (i = 0; i < sizeof(noDestList) / sizeof(noDestList[0]); ++i)
165
      opInfo[noDestList[i]].hasDest = 0;
166
   for (i = 0; i < sizeof(noPredList) / sizeof(noPredList[0]); ++i)
167
      opInfo[noPredList[i]].predicate = 0;
168
 
169
   for (i = 0; i < sizeof(_initProps) / sizeof(_initProps[0]); ++i) {
170
      const struct opProperties *prop = &_initProps[i];
171
 
172
      for (int s = 0; s < 3; ++s) {
173
         if (prop->mNeg & (1 << s))
174
            opInfo[prop->op].srcMods[s] |= NV50_IR_MOD_NEG;
175
         if (prop->mAbs & (1 << s))
176
            opInfo[prop->op].srcMods[s] |= NV50_IR_MOD_ABS;
177
         if (prop->mNot & (1 << s))
178
            opInfo[prop->op].srcMods[s] |= NV50_IR_MOD_NOT;
179
         if (prop->fConst & (1 << s))
180
            opInfo[prop->op].srcFiles[s] |= 1 << (int)FILE_MEMORY_CONST;
181
         if (prop->fShared & (1 << s))
182
            opInfo[prop->op].srcFiles[s] |= 1 << (int)FILE_MEMORY_SHARED;
183
         if (prop->fAttrib & (1 << s))
184
            opInfo[prop->op].srcFiles[s] |= 1 << (int)FILE_SHADER_INPUT;
185
         if (prop->fImm & (1 << s))
186
            opInfo[prop->op].srcFiles[s] |= 1 << (int)FILE_IMMEDIATE;
187
      }
188
      if (prop->mSat & 8)
189
         opInfo[prop->op].dstMods = NV50_IR_MOD_SAT;
190
   }
191
 
192
   if (chipset >= 0xa0)
193
      opInfo[OP_MUL].dstMods = NV50_IR_MOD_SAT;
194
}
195
 
196
unsigned int
197
TargetNV50::getFileSize(DataFile file) const
198
{
199
   switch (file) {
200
   case FILE_NULL:          return 0;
201
   case FILE_GPR:           return 256; // in 16-bit units **
202
   case FILE_PREDICATE:     return 0;
203
   case FILE_FLAGS:         return 4;
204
   case FILE_ADDRESS:       return 4;
205
   case FILE_IMMEDIATE:     return 0;
206
   case FILE_MEMORY_CONST:  return 65536;
207
   case FILE_SHADER_INPUT:  return 0x200;
208
   case FILE_SHADER_OUTPUT: return 0x200;
209
   case FILE_MEMORY_GLOBAL: return 0xffffffff;
210
   case FILE_MEMORY_SHARED: return 16 << 10;
211
   case FILE_MEMORY_LOCAL:  return 48 << 10;
212
   case FILE_SYSTEM_VALUE:  return 16;
213
   default:
214
      assert(!"invalid file");
215
      return 0;
216
   }
217
   // ** only first 128 units encodable for 16-bit regs
218
}
219
 
220
unsigned int
221
TargetNV50::getFileUnit(DataFile file) const
222
{
223
   if (file == FILE_GPR || file == FILE_ADDRESS)
224
      return 1;
225
   if (file == FILE_SYSTEM_VALUE)
226
      return 2;
227
   return 0;
228
}
229
 
230
uint32_t
231
TargetNV50::getSVAddress(DataFile shaderFile, const Symbol *sym) const
232
{
233
   switch (sym->reg.data.sv.sv) {
234
   case SV_FACE:
235
      return 0x3fc;
236
   case SV_POSITION:
237
   {
238
      uint32_t addr = sysvalLocation[sym->reg.data.sv.sv];
239
      for (int c = 0; c < sym->reg.data.sv.index; ++c)
240
         if (wposMask & (1 << c))
241
            addr += 4;
242
      return addr;
243
   }
244
   case SV_PRIMITIVE_ID:
245
      return shaderFile == FILE_SHADER_INPUT ? 0x18 :
246
         sysvalLocation[sym->reg.data.sv.sv];
247
   case SV_NCTAID:
248
      return 0x8 + 2 * sym->reg.data.sv.index;
249
   case SV_CTAID:
250
      return 0xc + 2 * sym->reg.data.sv.index;
251
   case SV_NTID:
252
      return 0x2 + 2 * sym->reg.data.sv.index;
253
   case SV_TID:
254
      return 0;
255
   case SV_SAMPLE_POS:
256
      return 0; /* sample position is handled differently */
257
   default:
258
      return sysvalLocation[sym->reg.data.sv.sv];
259
   }
260
}
261
 
262
// long:  rrr, arr, rcr, acr, rrc, arc, gcr, grr
263
// short: rr, ar, rc, gr
264
// immd:  ri, gi
265
bool
266
TargetNV50::insnCanLoad(const Instruction *i, int s,
267
                        const Instruction *ld) const
268
{
269
   DataFile sf = ld->src(0).getFile();
270
 
271
   if (sf == FILE_IMMEDIATE && (i->predSrc >= 0 || i->flagsDef >= 0))
272
      return false;
273
   if (s >= opInfo[i->op].srcNr)
274
      return false;
275
   if (!(opInfo[i->op].srcFiles[s] & (1 << (int)sf)))
276
      return false;
277
   if (s == 2 && i->src(1).getFile() != FILE_GPR)
278
      return false;
279
 
280
   // NOTE: don't rely on flagsDef
281
   if (sf == FILE_IMMEDIATE)
282
      for (int d = 0; i->defExists(d); ++d)
283
         if (i->def(d).getFile() == FILE_FLAGS)
284
            return false;
285
 
286
   unsigned mode = 0;
287
 
288
   for (int z = 0; z < Target::operationSrcNr[i->op]; ++z) {
289
      DataFile zf = (z == s) ? sf : i->src(z).getFile();
290
      switch (zf) {
291
      case FILE_GPR:
292
         break;
293
      case FILE_MEMORY_SHARED:
294
      case FILE_SHADER_INPUT:
295
         mode |= 1 << (z * 2);
296
         break;
297
      case FILE_MEMORY_CONST:
298
         mode |= 2 << (z * 2);
299
         break;
300
      case FILE_IMMEDIATE:
301
         mode |= 3 << (z * 2);
302
      default:
303
         break;
304
      }
305
   }
306
 
307
   switch (mode) {
308
   case 0x00:
309
   case 0x01:
310
   case 0x03:
311
   case 0x08:
312
   case 0x0c:
313
   case 0x20:
314
   case 0x21:
315
      break;
316
   case 0x09:
317
      // Shader inputs get transformed to p[] in geometry shaders, and those
318
      // aren't allowed to be used at the same time as c[].
319
      if (ld->bb->getProgram()->getType() == Program::TYPE_GEOMETRY)
320
         return false;
321
      break;
322
   case 0x0d:
323
      if (ld->bb->getProgram()->getType() != Program::TYPE_GEOMETRY)
324
         return false;
325
      break;
326
   default:
327
      return false;
328
   }
329
 
330
   uint8_t ldSize;
331
 
332
   if ((i->op == OP_MUL || i->op == OP_MAD) && !isFloatType(i->dType)) {
333
      // 32-bit MUL will be split into 16-bit MULs
334
      if (ld->src(0).isIndirect(0))
335
         return false;
336
      if (sf == FILE_IMMEDIATE)
337
         return false;
338
      if (i->subOp == NV50_IR_SUBOP_MUL_HIGH && sf == FILE_MEMORY_CONST)
339
         return false;
340
      ldSize = 2;
341
   } else {
342
      ldSize = typeSizeof(ld->dType);
343
   }
344
 
345
   if (sf == FILE_IMMEDIATE)
346
      return true;
347
 
348
 
349
   // Check if memory access is encodable:
350
 
351
   if (ldSize < 4 && sf == FILE_SHADER_INPUT) // no < 4-byte aligned a[] access
352
      return false;
353
   if (ld->getSrc(0)->reg.data.offset > (int32_t)(127 * ldSize))
354
      return false;
355
 
356
   if (ld->src(0).isIndirect(0)) {
357
      for (int z = 0; i->srcExists(z); ++z)
358
         if (i->src(z).isIndirect(0))
359
            return false;
360
 
361
      // s[] access only possible in CP, $aX always applies
362
      if (sf == FILE_MEMORY_SHARED)
363
         return true;
364
      if (!ld->bb) // can't check type ...
365
         return false;
366
      Program::Type pt = ld->bb->getProgram()->getType();
367
 
368
      // $aX applies to c[] only in VP, FP, GP if p[] is not accessed
369
      if (pt == Program::TYPE_COMPUTE)
370
         return false;
371
      if (pt == Program::TYPE_GEOMETRY) {
372
         if (sf == FILE_MEMORY_CONST)
373
            return i->src(s).getFile() != FILE_SHADER_INPUT;
374
         return sf == FILE_SHADER_INPUT;
375
      }
376
      return sf == FILE_MEMORY_CONST;
377
   }
378
   return true;
379
}
380
 
381
bool
382
TargetNV50::isAccessSupported(DataFile file, DataType ty) const
383
{
384
   if (ty == TYPE_B96 || ty == TYPE_NONE)
385
      return false;
386
   if (typeSizeof(ty) > 4)
387
      return (file == FILE_MEMORY_LOCAL) || (file == FILE_MEMORY_GLOBAL);
388
   return true;
389
}
390
 
391
bool
392
TargetNV50::isOpSupported(operation op, DataType ty) const
393
{
394
   if (ty == TYPE_F64 && chipset < 0xa0)
395
      return false;
396
 
397
   switch (op) {
398
   case OP_PRERET:
399
      return chipset >= 0xa0;
400
   case OP_TXG:
401
      return chipset >= 0xa3 && chipset != 0xaa && chipset != 0xac;
402
   case OP_POW:
403
   case OP_SQRT:
404
   case OP_DIV:
405
   case OP_MOD:
406
   case OP_SET_AND:
407
   case OP_SET_OR:
408
   case OP_SET_XOR:
409
   case OP_SLCT:
410
   case OP_SELP:
411
   case OP_POPCNT:
412
   case OP_INSBF:
413
   case OP_EXTBF:
414
   case OP_EXIT: // want exit modifier instead (on NOP if required)
415
   case OP_MEMBAR:
416
      return false;
417
   case OP_SAD:
418
      return ty == TYPE_S32;
419
   default:
420
      return true;
421
   }
422
}
423
 
424
bool
425
TargetNV50::isModSupported(const Instruction *insn, int s, Modifier mod) const
426
{
427
   if (!isFloatType(insn->dType)) {
428
      switch (insn->op) {
429
      case OP_ABS:
430
      case OP_NEG:
431
      case OP_CVT:
432
      case OP_CEIL:
433
      case OP_FLOOR:
434
      case OP_TRUNC:
435
      case OP_AND:
436
      case OP_OR:
437
      case OP_XOR:
438
         break;
439
      case OP_ADD:
440
         if (insn->src(s ? 0 : 1).mod.neg())
441
            return false;
442
         break;
443
      case OP_SUB:
444
         if (s == 0)
445
            return insn->src(1).mod.neg() ? false : true;
446
         break;
447
      case OP_SET:
448
         if (insn->sType != TYPE_F32)
449
            return false;
450
         break;
451
      default:
452
         return false;
453
      }
454
   }
455
   if (s >= 3)
456
      return false;
457
   return (mod & Modifier(opInfo[insn->op].srcMods[s])) == mod;
458
}
459
 
460
bool
461
TargetNV50::mayPredicate(const Instruction *insn, const Value *pred) const
462
{
463
   if (insn->getPredicate() || insn->flagsSrc >= 0)
464
      return false;
465
   for (int s = 0; insn->srcExists(s); ++s)
466
      if (insn->src(s).getFile() == FILE_IMMEDIATE)
467
         return false;
468
   return opInfo[insn->op].predicate;
469
}
470
 
471
bool
472
TargetNV50::isSatSupported(const Instruction *insn) const
473
{
474
   if (insn->op == OP_CVT)
475
      return true;
476
   if (insn->dType != TYPE_F32)
477
      return false;
478
   return opInfo[insn->op].dstMods & NV50_IR_MOD_SAT;
479
}
480
 
481
int TargetNV50::getLatency(const Instruction *i) const
482
{
483
   // TODO: tune these values
484
   if (i->op == OP_LOAD) {
485
      switch (i->src(0).getFile()) {
486
      case FILE_MEMORY_LOCAL:
487
      case FILE_MEMORY_GLOBAL:
488
         return 100; // really 400 to 800
489
      default:
490
         return 22;
491
      }
492
   }
493
   return 22;
494
}
495
 
496
// These are "inverse" throughput values, i.e. the number of cycles required
497
// to issue a specific instruction for a full warp (32 threads).
498
//
499
// Assuming we have more than 1 warp in flight, a higher issue latency results
500
// in a lower result latency since the MP will have spent more time with other
501
// warps.
502
// This also helps to determine the number of cycles between instructions in
503
// a single warp.
504
//
505
int TargetNV50::getThroughput(const Instruction *i) const
506
{
507
   // TODO: tune these values
508
   if (i->dType == TYPE_F32) {
509
      switch (i->op) {
510
      case OP_RCP:
511
      case OP_RSQ:
512
      case OP_LG2:
513
      case OP_SIN:
514
      case OP_COS:
515
      case OP_PRESIN:
516
      case OP_PREEX2:
517
         return 16;
518
      default:
519
         return 4;
520
      }
521
   } else
522
   if (i->dType == TYPE_U32 || i->dType == TYPE_S32) {
523
      return 4;
524
   } else
525
   if (i->dType == TYPE_F64) {
526
      return 32;
527
   } else {
528
      return 1;
529
   }
530
}
531
 
532
static void
533
recordLocation(uint16_t *locs, uint8_t *masks,
534
               const struct nv50_ir_varying *var)
535
{
536
   uint16_t addr = var->slot[0] * 4;
537
 
538
   switch (var->sn) {
539
   case TGSI_SEMANTIC_POSITION: locs[SV_POSITION] = addr; break;
540
   case TGSI_SEMANTIC_INSTANCEID: locs[SV_INSTANCE_ID] = addr; break;
541
   case TGSI_SEMANTIC_VERTEXID: locs[SV_VERTEX_ID] = addr; break;
542
   case TGSI_SEMANTIC_PRIMID: locs[SV_PRIMITIVE_ID] = addr; break;
543
   case TGSI_SEMANTIC_LAYER: locs[SV_LAYER] = addr; break;
544
   case TGSI_SEMANTIC_VIEWPORT_INDEX: locs[SV_VIEWPORT_INDEX] = addr; break;
545
   default:
546
      break;
547
   }
548
   if (var->sn == TGSI_SEMANTIC_POSITION && masks)
549
      masks[0] = var->mask;
550
}
551
 
552
void
553
TargetNV50::parseDriverInfo(const struct nv50_ir_prog_info *info)
554
{
555
   unsigned int i;
556
   for (i = 0; i < info->numOutputs; ++i)
557
      recordLocation(sysvalLocation, NULL, &info->out[i]);
558
   for (i = 0; i < info->numInputs; ++i)
559
      recordLocation(sysvalLocation, &wposMask, &info->in[i]);
560
   for (i = 0; i < info->numSysVals; ++i)
561
      recordLocation(sysvalLocation, NULL, &info->sv[i]);
562
 
563
   if (sysvalLocation[SV_POSITION] >= 0x200) {
564
      // not assigned by driver, but we need it internally
565
      wposMask = 0x8;
566
      sysvalLocation[SV_POSITION] = 0;
567
   }
568
}
569
 
570
} // namespace nv50_ir