1
/**
2
 * Declarations for ptrntab.d, the instruction tables for the inline assembler.
3
 *
4
 * Copyright:   Copyright (C) 1982-1998 by Symantec
5
 *              Copyright (C) 2000-2020 by The D Language Foundation, All Rights Reserved
6
 * Authors:     Mike Cote, John Micco, $(LINK2 http://www.digitalmars.com, Walter Bright),
7
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/iasm.d, backend/iasm.d)
9
 * Documentation:  https://dlang.org/phobos/dmd_backend_iasm.html
10
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/iasm.d
11
 */
12

13
module dmd.backend.iasm;
14

15
// Online documentation: https://dlang.org/phobos/dmd_backend_iasm.html
16

17
import dmd.backend.cc : block;
18
import dmd.backend.code_x86 : opcode_t;
19

20
extern (C++):
21
@nogc:
22
nothrow:
23

24
//#include <setjmp.h>
25

26
/////////////////////////////////////////////////
27
// Instruction flags (usFlags)
28
//
29
//
30

31
enum _modrm = 0x10;
32

33
// This is for when the reg field of modregrm specifies which instruction it is
34
enum
35
{
36
    NUM_MASK  = 0x7,
37
    NUM_MASKR = 0x8,             // for REX extended registers
38
    _0      = (0x0 | _modrm),    // insure that some _modrm bit is set
39
    _1      = 0x1,               // with _0
40
    _2      = 0x2,
41
    _3      = 0x3,
42
    _4      = 0x4,
43
    _5      = 0x5,
44
    _6      = 0x6,
45
    _7      = 0x7,
46
}
47

48
enum
49
{
50
    _r           = _modrm,
51
    _cb          = _modrm,
52
    _cw          = _modrm,
53
    _cd          = _modrm,
54
    _cq          = _modrm,
55
    _cp          = _modrm,
56
    _ib          = 0,
57
    _iw          = 0,
58
    _id          = 0,
59
    _rb          = 0,
60
    _rw          = 0,
61
    _rd          = 0,
62
    _16_bit      = 0x20,
63
    _32_bit      = 0x40,
64
    _64_bit      = 0x10000,
65
    _i64_bit     = 0x20000,  // opcode is invalid in 64bit mode
66
    _I386        = 0x80,     // opcode is only for 386 and later
67
    _16_bit_addr = 0x100,
68
    _32_bit_addr = 0x200,
69
    _fwait       = 0x400,    // Add an FWAIT prior to the instruction opcode
70
    _nfwait      = 0x800,    // Do not add an FWAIT prior to the instruction
71
}
72

73
enum
74
{
75
    MOD_MASK        = 0xF000,  // Mod mask
76
    _modsi          = 0x1000,  // Instruction modifies SI
77
    _moddx          = 0x2000,  // Instruction modifies DX
78
    _mod2           = 0x3000,  // Instruction modifies second operand
79
    _modax          = 0x4000,  // Instruction modifies AX
80
    _modnot1        = 0x5000,  // Instruction does not modify first operand
81
    _modaxdx        = 0x6000,  // instruction modifies AX and DX
82
    _moddi          = 0x7000,  // Instruction modifies DI
83
    _modsidi        = 0x8000,  // Instruction modifies SI and DI
84
    _modcx          = 0x9000,  // Instruction modifies CX
85
    _modes          = 0xa000,  // Instruction modifies ES
86
    _modall         = 0xb000,  // Instruction modifies all register values
87
    _modsiax        = 0xc000,  // Instruction modifies AX and SI
88
    _modsinot1      = 0xd000,  // Instruction modifies SI and not first param
89
    _modcxr11       = 0xe000,  // Instruction modifies CX and R11
90
    _modxmm0        = 0xf000,  // Instruction modifies XMM0
91
}
92

93
// translates opcode into equivalent vex encoding
94 0
uint VEX_128_W0(opcode_t op)            { return _VEX(op)|_VEX_NOO; }
95 0
uint VEX_128_W1(opcode_t op)            { return _VEX(op)|_VEX_NOO|_VEX_W; }
96 0
uint VEX_128_WIG(opcode_t op)           { return  VEX_128_W0(op); }
97 0
uint VEX_256_W0(opcode_t op)            { return _VEX(op)|_VEX_NOO|_VEX_L; }
98 0
uint VEX_256_W1(opcode_t op)            { return _VEX(op)|_VEX_NOO|_VEX_W|_VEX_L; }
99 0
uint VEX_256_WIG(opcode_t op)           { return  VEX_256_W0(op); }
100 0
uint VEX_NDS_128_W0(opcode_t op)        { return _VEX(op)|_VEX_NDS; }
101 0
uint VEX_NDS_128_W1(opcode_t op)        { return _VEX(op)|_VEX_NDS|_VEX_W; }
102 0
uint VEX_NDS_128_WIG(opcode_t op)       { return  VEX_NDS_128_W0(op); }
103 0
uint VEX_NDS_256_W0(opcode_t op)        { return _VEX(op)|_VEX_NDS|_VEX_L; }
104 0
uint VEX_NDS_256_W1(opcode_t op)        { return _VEX(op)|_VEX_NDS|_VEX_W|_VEX_L; }
105 0
uint VEX_NDS_256_WIG(opcode_t op)       { return  VEX_NDS_256_W0(op); }
106 0
uint VEX_NDD_128_W0(opcode_t op)        { return _VEX(op)|_VEX_NDD; }
107 0
uint VEX_NDD_128_W1(opcode_t op)        { return _VEX(op)|_VEX_NDD|_VEX_W; }
108 0
uint VEX_NDD_128_WIG(opcode_t op)       { return  VEX_NDD_128_W0(op); }
109 0
uint VEX_NDD_256_W0(opcode_t op)        { return _VEX(op)|_VEX_NDD|_VEX_L; }
110 0
uint VEX_NDD_256_W1(opcode_t op)        { return _VEX(op)|_VEX_NDD|_VEX_W|_VEX_L; }
111 0
uint VEX_NDD_256_WIG(opcode_t op)       { return  VEX_NDD_256_W0(op); }
112 0
uint VEX_DDS_128_W0(opcode_t op)        { return _VEX(op)|_VEX_DDS; }
113 0
uint VEX_DDS_128_W1(opcode_t op)        { return _VEX(op)|_VEX_DDS|_VEX_W; }
114 0
uint VEX_DDS_128_WIG(opcode_t op)       { return  VEX_DDS_128_W0(op); }
115 0
uint VEX_DDS_256_W0(opcode_t op)        { return _VEX(op)|_VEX_DDS|_VEX_L; }
116 0
uint VEX_DDS_256_W1(opcode_t op)        { return _VEX(op)|_VEX_DDS|_VEX_W|_VEX_L; }
117 0
uint VEX_DDS_256_WIG(opcode_t op)       { return  VEX_DDS_256_W0(op); }
118

119
enum _VEX_W   = 0x8000;
120
/* Don't encode LIG/LZ use 128 for these.
121
 */
122
enum _VEX_L   = 0x0400;
123
/* Encode nds, ndd, dds in the vvvv field, it gets
124
 * overwritten with the actual register later.
125
 */
126
enum
127
{
128
     VEX_NOO = 0, // neither of nds, ndd, dds
129
     VEX_NDS = 1,
130
     VEX_NDD = 2,
131
     VEX_DDS = 3,
132
    _VEX_NOO  = VEX_NOO << 11,
133
    _VEX_NDS  = VEX_NDS << 11,
134
    _VEX_NDD  = VEX_NDD << 11,
135
    _VEX_DDS  = VEX_DDS << 11,
136
}
137

138 0
uint _VEX(opcode_t op) { return (0xC4 << 24) | _VEX_MM(op >> 8) | (op & 0xFF); }
139

140
uint _VEX_MM(opcode_t op)
141
{
142 0
    return
143 0
        (op & 0x00FF) == 0x000F ? (0x1 << 16 | _VEX_PP(op >>  8)) :
144 0
        (op & 0xFFFF) == 0x0F38 ? (0x2 << 16 | _VEX_PP(op >> 16)) :
145 0
        (op & 0xFFFF) == 0x0F3A ? (0x3 << 16 | _VEX_PP(op >> 16)) :
146 0
        _VEX_ASSERT0;
147
}
148

149
uint _VEX_PP(opcode_t op)
150
{
151 0
    return
152
        op == 0x00 ? 0x00 << 8 :
153 0
        op == 0x66 ? 0x01 << 8 :
154 0
        op == 0xF3 ? 0x02 << 8 :
155 0
        op == 0xF2 ? 0x03 << 8 :
156 0
        _VEX_ASSERT0;
157
}
158

159
// avoid dynamic initialization of the asm tables
160
debug
161
{
162 0
    @property uint _VEX_ASSERT0() { assert(0); }
163
}
164
else
165
{
166
    @property uint _VEX_ASSERT0() { return 0; }
167
}
168

169

170
/////////////////////////////////////////////////
171
// Operand flags - usOp1, usOp2, usOp3
172
//
173

174
alias opflag_t = uint;
175

176
// Operand flags for normal opcodes
177
enum
178
{
179
    _r8     = CONSTRUCT_FLAGS(OpndSize._8, _reg, _normal, 0 ),
180
    _r16    = CONSTRUCT_FLAGS(OpndSize._16, _reg, _normal, 0 ),
181
    _r32    = CONSTRUCT_FLAGS(OpndSize._32, _reg, _normal, 0 ),
182
    _r64    = CONSTRUCT_FLAGS(OpndSize._64, _reg, _normal, 0 ),
183
    _m8     = CONSTRUCT_FLAGS(OpndSize._8, _m, _normal, 0 ),
184
    _m16    = CONSTRUCT_FLAGS(OpndSize._16, _m, _normal, 0 ),
185
    _m32    = CONSTRUCT_FLAGS(OpndSize._32, _m, _normal, 0 ),
186
    _m48    = CONSTRUCT_FLAGS(OpndSize._48, _m, _normal, 0 ),
187
    _m64    = CONSTRUCT_FLAGS(OpndSize._64, _m, _normal, 0 ),
188
    _m128   = CONSTRUCT_FLAGS(OpndSize._128, _m, _normal, 0 ),
189
    _m256   = CONSTRUCT_FLAGS(OpndSize._anysize, _m, _normal, 0 ),
190
    _m48_32_16_8    = CONSTRUCT_FLAGS(OpndSize._48_32_16_8, _m, _normal, 0 ),
191
    _m64_48_32_16_8 = CONSTRUCT_FLAGS(OpndSize._64_48_32_16_8, _m, _normal, 0 ),
192
    _rm8    = CONSTRUCT_FLAGS(OpndSize._8, _rm, _normal, 0 ),
193
    _rm16   = CONSTRUCT_FLAGS(OpndSize._16, _rm, _normal, 0 ),
194
    _rm32   = CONSTRUCT_FLAGS(OpndSize._32, _rm, _normal, 0),
195
    _rm64   = CONSTRUCT_FLAGS(OpndSize._64, _rm, _normal, 0),
196
    _r32m8  = CONSTRUCT_FLAGS(OpndSize._32_8, _rm, _normal, 0),
197
    _r32m16 = CONSTRUCT_FLAGS(OpndSize._32_16, _rm, _normal, 0),
198
    _regm8  = CONSTRUCT_FLAGS(OpndSize._64_32_8, _rm, _normal, 0),
199
    _imm8   = CONSTRUCT_FLAGS(OpndSize._8, _imm, _normal, 0 ),
200
    _imm16  = CONSTRUCT_FLAGS(OpndSize._16, _imm, _normal, 0),
201
    _imm32  = CONSTRUCT_FLAGS(OpndSize._32, _imm, _normal, 0),
202
    _imm64  = CONSTRUCT_FLAGS(OpndSize._64, _imm, _normal, 0),
203
    _rel8   = CONSTRUCT_FLAGS(OpndSize._8, _rel, _normal, 0),
204
    _rel16  = CONSTRUCT_FLAGS(OpndSize._16, _rel, _normal, 0),
205
    _rel32  = CONSTRUCT_FLAGS(OpndSize._32, _rel, _normal, 0),
206
    _p1616  = CONSTRUCT_FLAGS(OpndSize._32, _p, _normal, 0),
207
    _m1616  = CONSTRUCT_FLAGS(OpndSize._32, _mnoi, _normal, 0),
208
    _p1632  = CONSTRUCT_FLAGS(OpndSize._48, _p, _normal, 0 ),
209
    _m1632  = CONSTRUCT_FLAGS(OpndSize._48, _mnoi, _normal, 0),
210
    _special  = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0 ),
211
    _seg    = CONSTRUCT_FLAGS( 0, 0, _rseg, 0 ),
212
    _a16    = CONSTRUCT_FLAGS( 0, 0, _addr16, 0 ),
213
    _a32    = CONSTRUCT_FLAGS( 0, 0, _addr32, 0 ),
214
    _f16    = CONSTRUCT_FLAGS( 0, 0, _fn16, 0),
215
                                                // Near function pointer
216
    _f32    = CONSTRUCT_FLAGS( 0, 0, _fn32, 0),
217
                                                // Far function pointer
218
    _lbl    = CONSTRUCT_FLAGS( 0, 0, _flbl, 0 ),
219
                                                // Label (in current function)
220

221
    _mmm32  = CONSTRUCT_FLAGS( 0, _m, 0, OpndSize._32),
222
    _mmm64  = CONSTRUCT_FLAGS( OpndSize._64, _m, 0, _f64),
223
    _mmm128 = CONSTRUCT_FLAGS( 0, _m, 0, _f128),
224

225
    _xmm_m16  = CONSTRUCT_FLAGS( OpndSize._16,      _m, _rspecial, ASM_GET_uRegmask(_xmm)),
226
    _xmm_m32  = CONSTRUCT_FLAGS( OpndSize._32,      _m, _rspecial, ASM_GET_uRegmask(_xmm)),
227
    _xmm_m64  = CONSTRUCT_FLAGS( OpndSize._anysize, _m, _rspecial, ASM_GET_uRegmask(_xmm)),
228
    _xmm_m128 = CONSTRUCT_FLAGS( OpndSize._128,     _m, _rspecial, ASM_GET_uRegmask(_xmm)),
229
    _ymm_m256 = CONSTRUCT_FLAGS( OpndSize._anysize, _m, _rspecial, ASM_GET_uRegmask(_ymm)),
230

231
    _moffs8  = _rel8,
232
    _moffs16 = _rel16,
233
    _moffs32 = _rel32,
234
}
235

236
////////////////////////////////////////////////////////////////////
237
// Operand flags for floating point opcodes are all just aliases for
238
// normal opcode variants and only asm_determine_operator_flags should
239
// need to care.
240

241
enum
242
{
243
    _fm80   = CONSTRUCT_FLAGS( 0, _m, 0, _f80 ),
244
    _fm64   = CONSTRUCT_FLAGS( 0, _m, 0, _f64 ),
245
    _fm128  = CONSTRUCT_FLAGS( 0, _m, 0, _f128 ),
246
    _fanysize = (_f64 | _f80 | _f112 ),
247

248
    _float_m = CONSTRUCT_FLAGS( OpndSize._anysize, _float, 0, _fanysize),
249

250
    _st     = CONSTRUCT_FLAGS( 0, _float, 0, _rst ),   // stack register 0
251
    _m112   = CONSTRUCT_FLAGS( 0, _m, 0, _f112 ),
252
    _m224   = _m112,
253
    _m512   = _m224,
254
    _sti    = CONSTRUCT_FLAGS( 0, _float, 0, _rsti ),
255
}
256

257
////////////////// FLAGS /////////////////////////////////////
258

259
// bit size                      5            3          3              7
260
opflag_t CONSTRUCT_FLAGS(uint uSizemask, uint aopty, uint amod, uint uRegmask)
261
{
262 1
    return uSizemask | (aopty << 5) | (amod << 8) | (uRegmask << 11);
263
}
264

265 1
uint ASM_GET_aopty(uint us)     { return cast(ASM_OPERAND_TYPE)((us >> 5) & 7); }
266 1
uint ASM_GET_amod(uint us)      { return cast(ASM_MODIFIERS)((us >> 8) & 7); }
267 1
uint ASM_GET_uRegmask(uint us)  { return (us >> 11) & 0x7F; }
268

269
// For uSizemask (5 bits)
270
enum OpndSize : ubyte
271
{
272
    none = 0,
273

274
    _8,  // 0x1,
275
    _16, // 0x2,
276
    _32, // 0x4,
277
    _48, // 0x8,
278
    _64, // 0x10,
279
    _128, // 0x20,
280

281
    _16_8,       // _16 | _8,
282
    _32_8,       // _32 | _8,
283
    _32_16,      // _32 | _16,
284
    _32_16_8,    // _32 | _16 | _8,
285
    _48_32,      // _48 | _32,
286
    _48_32_16_8, // _48 | _32 | _16 | _8,
287
    _64_32,      // _64 | _32,
288
    _64_32_8,    // _64 | _32 | _8,
289
    _64_32_16,   // _64 | _32 | _16,
290
    _64_32_16_8, // _64 | _32 | _16 | _8,
291
    _64_48_32_16_8, // _64 | _48 | _32 | _16 | _8,
292

293
    _anysize,
294
}
295

296
/*************************************
297
 * Extract OpndSize from opflag_t.
298
 */
299 1
OpndSize getOpndSize(opflag_t us) { return cast(OpndSize) (us & 0x1F); }
300

301
// For aopty (3 bits)
302
alias ASM_OPERAND_TYPE = uint;
303
enum
304
{
305
    _reg,           // _r8, _r16, _r32
306
    _m,             // _m8, _m16, _m32, _m48
307
    _imm,           // _imm8, _imm16, _imm32, _imm64
308
    _rel,           // _rel8, _rel16, _rel32
309
    _mnoi,          // _m1616, _m1632
310
    _p,             // _p1616, _p1632
311
    _rm,            // _rm8, _rm16, _rm32
312
    _float          // Floating point operand, look at cRegmask for the
313
                    // actual size
314
}
315

316
// For amod (3 bits)
317
alias ASM_MODIFIERS = uint;
318
enum
319
{
320
    _normal,        // Normal register value
321
    _rseg,          // Segment registers
322
    _rspecial,      // Special registers
323
    _addr16,        // 16 bit address
324
    _addr32,        // 32 bit address
325
    _fn16,          // 16 bit function call
326
    _fn32,          // 32 bit function call
327
    _flbl           // Label
328
}
329

330
// For uRegmask (7 bits)
331

332
// uRegmask flags when aopty == _float
333
enum
334
{
335
    _rst    = 0x1,
336
    _rsti   = 0x2,
337
    _f64    = 0x4,
338
    _f80    = 0x8,
339
    _f112   = 0x10,
340
    _f128   = 0x20,
341
}
342

343
// _seg register values (amod == _rseg)
344
//
345
enum
346
{
347
    _ds     = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x01 ),
348
    _es     = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x02 ),
349
    _ss     = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x04 ),
350
    _fs     = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x08 ),
351
    _gs     = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x10 ),
352
    _cs     = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x20 ),
353
}
354

355
//
356
// _special register values
357
//
358
enum
359
{
360
    _crn    = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x01 ), // CRn register (0,2,3)
361
    _drn    = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x02 ), // DRn register (0-3,6-7)
362
    _trn    = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x04 ), // TRn register (3-7)
363
    _mm     = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x08 ), // MMn register (0-7)
364
    _xmm    = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x10 ), // XMMn register (0-7)
365
    _xmm0   = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x20 ), // XMM0 register
366
    _ymm    = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x40 ), // YMMn register (0-15)
367
}
368

369
//
370
// Default register values
371
//
372
enum
373
{
374
    _al     = CONSTRUCT_FLAGS( 0, 0, _normal, 0x01 ),  // AL register
375
    _ax     = CONSTRUCT_FLAGS( 0, 0, _normal, 0x02 ),  // AX register
376
    _eax    = CONSTRUCT_FLAGS( 0, 0, _normal, 0x04 ),  // EAX register
377
    _dx     = CONSTRUCT_FLAGS( 0, 0, _normal, 0x08 ),  // DX register
378
    _cl     = CONSTRUCT_FLAGS( 0, 0, _normal, 0x10 ),  // CL register
379
    _rax    = CONSTRUCT_FLAGS( 0, 0, _normal, 0x40 ),  // RAX register
380
}
381

382

383
enum _rplus_r        = 0x20;
384
enum _plus_r = CONSTRUCT_FLAGS( 0, 0, 0, _rplus_r );
385
                // Add the register to the opcode (no mod r/m)
386

387

388

389
//////////////////////////////////////////////////////////////////
390

391
enum
392
{
393
    ITprefix        = 0x10,    // special prefix
394
    ITjump          = 0x20,    // jump instructions CALL, Jxx and LOOPxx
395
    ITimmed         = 0x30,    // value of an immediate operand controls
396
                               // code generation
397
    ITopt           = 0x40,    // not all operands are required
398
    ITshift         = 0x50,    // rotate and shift instructions
399
    ITfloat         = 0x60,    // floating point coprocessor instructions
400
    ITdata          = 0x70,    // DB, DW, DD, DQ, DT pseudo-ops
401
    ITaddr          = 0x80,    // DA (define addresss) pseudo-op
402
    ITMASK          = 0xF0,
403
    ITSIZE          = 0x0F,    // mask for size
404
}
405

406
version (SCPP)
407
{
408
    alias OP_DB = int;
409
    enum
410
    {
411
        // These are the number of bytes
412
        OPdb = 1,
413
        OPdw = 2,
414
        OPdd = 4,
415
        OPdq = 8,
416
        OPdt = 10,
417
        OPdf = 4,
418
        OPde = 10,
419
        OPds = 2,
420
        OPdi = 4,
421
        OPdl = 8,
422
    }
423
}
424
version (MARS)
425
{
426
    alias OP_DB = int;
427
    enum
428
    {
429
        // Integral types
430
        OPdb,
431
        OPds,
432
        OPdi,
433
        OPdl,
434

435
        // Float types
436
        OPdf,
437
        OPdd,
438
        OPde,
439

440
        // Deprecated
441
        OPdw = OPds,
442
        OPdq = OPdl,
443
        OPdt = OPde,
444
    }
445
}
446

447

448
/* from iasm.c */
449
int asm_state(int iFlags);
450

451
void asm_process_fixup( block **ppblockLabels );
452

453
struct PTRNTAB4
454
{
455
        opcode_t opcode;
456
        uint usFlags;
457
        opflag_t usOp1;
458
        opflag_t usOp2;
459
        opflag_t usOp3;
460
        opflag_t usOp4;
461
}
462

463
struct PTRNTAB3 {
464
        opcode_t opcode;
465
        uint usFlags;
466
        opflag_t usOp1;
467
        opflag_t usOp2;
468
        opflag_t usOp3;
469
}
470

471
struct PTRNTAB2 {
472
        opcode_t opcode;
473
        uint usFlags;
474
        opflag_t usOp1;
475
        opflag_t usOp2;
476
}
477

478
struct PTRNTAB1 {
479
        opcode_t opcode;
480
        uint usFlags;
481
        opflag_t usOp1;
482
}
483

484
enum ASM_END = 0xffff;      // special opcode meaning end of PTRNTABx table
485

486
struct PTRNTAB0 {
487
        opcode_t opcode;
488
        uint usFlags;
489
}
490

491
union PTRNTAB {
492
        void            *ppt;
493
        PTRNTAB0        *pptb0;
494
        PTRNTAB1        *pptb1;
495
        PTRNTAB2        *pptb2;
496
        PTRNTAB3        *pptb3;
497
        PTRNTAB4        *pptb4;
498
}
499

500
struct OP
501
{
502
    const(char)* str;   // opcode string
503
    ubyte usNumops;
504
    PTRNTAB ptb;
505
}
506

Read our documentation on viewing source code .

Loading