podman

Форк
0
1261 строка · 33.4 Кб
1
// Inferno utils/6l/pass.c
2
// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/pass.c
3
//
4
//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
5
//	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
6
//	Portions Copyright © 1997-1999 Vita Nuova Limited
7
//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
8
//	Portions Copyright © 2004,2006 Bruce Ellis
9
//	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
10
//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
11
//	Portions Copyright © 2009 The Go Authors. All rights reserved.
12
//
13
// Permission is hereby granted, free of charge, to any person obtaining a copy
14
// of this software and associated documentation files (the "Software"), to deal
15
// in the Software without restriction, including without limitation the rights
16
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
// copies of the Software, and to permit persons to whom the Software is
18
// furnished to do so, subject to the following conditions:
19
//
20
// The above copyright notice and this permission notice shall be included in
21
// all copies or substantial portions of the Software.
22
//
23
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
26
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29
// THE SOFTWARE.
30

31
package x86
32

33
import (
34
	"github.com/twitchyliquid64/golang-asm/obj"
35
	"github.com/twitchyliquid64/golang-asm/objabi"
36
	"github.com/twitchyliquid64/golang-asm/src"
37
	"github.com/twitchyliquid64/golang-asm/sys"
38
	"math"
39
	"strings"
40
)
41

42
func CanUse1InsnTLS(ctxt *obj.Link) bool {
43
	if isAndroid {
44
		// Android uses a global variable for the tls offset.
45
		return false
46
	}
47

48
	if ctxt.Arch.Family == sys.I386 {
49
		switch ctxt.Headtype {
50
		case objabi.Hlinux,
51
			objabi.Hplan9,
52
			objabi.Hwindows:
53
			return false
54
		}
55

56
		return true
57
	}
58

59
	switch ctxt.Headtype {
60
	case objabi.Hplan9, objabi.Hwindows:
61
		return false
62
	case objabi.Hlinux, objabi.Hfreebsd:
63
		return !ctxt.Flag_shared
64
	}
65

66
	return true
67
}
68

69
func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
70
	// Thread-local storage references use the TLS pseudo-register.
71
	// As a register, TLS refers to the thread-local storage base, and it
72
	// can only be loaded into another register:
73
	//
74
	//         MOVQ TLS, AX
75
	//
76
	// An offset from the thread-local storage base is written off(reg)(TLS*1).
77
	// Semantically it is off(reg), but the (TLS*1) annotation marks this as
78
	// indexing from the loaded TLS base. This emits a relocation so that
79
	// if the linker needs to adjust the offset, it can. For example:
80
	//
81
	//         MOVQ TLS, AX
82
	//         MOVQ 0(AX)(TLS*1), CX // load g into CX
83
	//
84
	// On systems that support direct access to the TLS memory, this
85
	// pair of instructions can be reduced to a direct TLS memory reference:
86
	//
87
	//         MOVQ 0(TLS), CX // load g into CX
88
	//
89
	// The 2-instruction and 1-instruction forms correspond to the two code
90
	// sequences for loading a TLS variable in the local exec model given in "ELF
91
	// Handling For Thread-Local Storage".
92
	//
93
	// We apply this rewrite on systems that support the 1-instruction form.
94
	// The decision is made using only the operating system and the -shared flag,
95
	// not the link mode. If some link modes on a particular operating system
96
	// require the 2-instruction form, then all builds for that operating system
97
	// will use the 2-instruction form, so that the link mode decision can be
98
	// delayed to link time.
99
	//
100
	// In this way, all supported systems use identical instructions to
101
	// access TLS, and they are rewritten appropriately first here in
102
	// liblink and then finally using relocations in the linker.
103
	//
104
	// When -shared is passed, we leave the code in the 2-instruction form but
105
	// assemble (and relocate) them in different ways to generate the initial
106
	// exec code sequence. It's a bit of a fluke that this is possible without
107
	// rewriting the instructions more comprehensively, and it only does because
108
	// we only support a single TLS variable (g).
109

110
	if CanUse1InsnTLS(ctxt) {
111
		// Reduce 2-instruction sequence to 1-instruction sequence.
112
		// Sequences like
113
		//	MOVQ TLS, BX
114
		//	... off(BX)(TLS*1) ...
115
		// become
116
		//	NOP
117
		//	... off(TLS) ...
118
		//
119
		// TODO(rsc): Remove the Hsolaris special case. It exists only to
120
		// guarantee we are producing byte-identical binaries as before this code.
121
		// But it should be unnecessary.
122
		if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_REG && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 && ctxt.Headtype != objabi.Hsolaris {
123
			obj.Nopout(p)
124
		}
125
		if p.From.Type == obj.TYPE_MEM && p.From.Index == REG_TLS && REG_AX <= p.From.Reg && p.From.Reg <= REG_R15 {
126
			p.From.Reg = REG_TLS
127
			p.From.Scale = 0
128
			p.From.Index = REG_NONE
129
		}
130

131
		if p.To.Type == obj.TYPE_MEM && p.To.Index == REG_TLS && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 {
132
			p.To.Reg = REG_TLS
133
			p.To.Scale = 0
134
			p.To.Index = REG_NONE
135
		}
136
	} else {
137
		// load_g_cx, below, always inserts the 1-instruction sequence. Rewrite it
138
		// as the 2-instruction sequence if necessary.
139
		//	MOVQ 0(TLS), BX
140
		// becomes
141
		//	MOVQ TLS, BX
142
		//	MOVQ 0(BX)(TLS*1), BX
143
		if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 {
144
			q := obj.Appendp(p, newprog)
145
			q.As = p.As
146
			q.From = p.From
147
			q.From.Type = obj.TYPE_MEM
148
			q.From.Reg = p.To.Reg
149
			q.From.Index = REG_TLS
150
			q.From.Scale = 2 // TODO: use 1
151
			q.To = p.To
152
			p.From.Type = obj.TYPE_REG
153
			p.From.Reg = REG_TLS
154
			p.From.Index = REG_NONE
155
			p.From.Offset = 0
156
		}
157
	}
158

159
	// Android uses a tls offset determined at runtime. Rewrite
160
	//	MOVQ TLS, BX
161
	// to
162
	//	MOVQ runtime.tls_g(SB), BX
163
	if isAndroid && (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_REG && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 {
164
		p.From.Type = obj.TYPE_MEM
165
		p.From.Name = obj.NAME_EXTERN
166
		p.From.Reg = REG_NONE
167
		p.From.Sym = ctxt.Lookup("runtime.tls_g")
168
		p.From.Index = REG_NONE
169
	}
170

171
	// TODO: Remove.
172
	if ctxt.Headtype == objabi.Hwindows && ctxt.Arch.Family == sys.AMD64 || ctxt.Headtype == objabi.Hplan9 {
173
		if p.From.Scale == 1 && p.From.Index == REG_TLS {
174
			p.From.Scale = 2
175
		}
176
		if p.To.Scale == 1 && p.To.Index == REG_TLS {
177
			p.To.Scale = 2
178
		}
179
	}
180

181
	// Rewrite 0 to $0 in 3rd argument to CMPPS etc.
182
	// That's what the tables expect.
183
	switch p.As {
184
	case ACMPPD, ACMPPS, ACMPSD, ACMPSS:
185
		if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE && p.To.Reg == REG_NONE && p.To.Index == REG_NONE && p.To.Sym == nil {
186
			p.To.Type = obj.TYPE_CONST
187
		}
188
	}
189

190
	// Rewrite CALL/JMP/RET to symbol as TYPE_BRANCH.
191
	switch p.As {
192
	case obj.ACALL, obj.AJMP, obj.ARET:
193
		if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {
194
			p.To.Type = obj.TYPE_BRANCH
195
		}
196
	}
197

198
	// Rewrite MOVL/MOVQ $XXX(FP/SP) as LEAL/LEAQ.
199
	if p.From.Type == obj.TYPE_ADDR && (ctxt.Arch.Family == sys.AMD64 || p.From.Name != obj.NAME_EXTERN && p.From.Name != obj.NAME_STATIC) {
200
		switch p.As {
201
		case AMOVL:
202
			p.As = ALEAL
203
			p.From.Type = obj.TYPE_MEM
204
		case AMOVQ:
205
			p.As = ALEAQ
206
			p.From.Type = obj.TYPE_MEM
207
		}
208
	}
209

210
	// Rewrite float constants to values stored in memory.
211
	switch p.As {
212
	// Convert AMOVSS $(0), Xx to AXORPS Xx, Xx
213
	case AMOVSS:
214
		if p.From.Type == obj.TYPE_FCONST {
215
			//  f == 0 can't be used here due to -0, so use Float64bits
216
			if f := p.From.Val.(float64); math.Float64bits(f) == 0 {
217
				if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 {
218
					p.As = AXORPS
219
					p.From = p.To
220
					break
221
				}
222
			}
223
		}
224
		fallthrough
225

226
	case AFMOVF,
227
		AFADDF,
228
		AFSUBF,
229
		AFSUBRF,
230
		AFMULF,
231
		AFDIVF,
232
		AFDIVRF,
233
		AFCOMF,
234
		AFCOMFP,
235
		AADDSS,
236
		ASUBSS,
237
		AMULSS,
238
		ADIVSS,
239
		ACOMISS,
240
		AUCOMISS:
241
		if p.From.Type == obj.TYPE_FCONST {
242
			f32 := float32(p.From.Val.(float64))
243
			p.From.Type = obj.TYPE_MEM
244
			p.From.Name = obj.NAME_EXTERN
245
			p.From.Sym = ctxt.Float32Sym(f32)
246
			p.From.Offset = 0
247
		}
248

249
	case AMOVSD:
250
		// Convert AMOVSD $(0), Xx to AXORPS Xx, Xx
251
		if p.From.Type == obj.TYPE_FCONST {
252
			//  f == 0 can't be used here due to -0, so use Float64bits
253
			if f := p.From.Val.(float64); math.Float64bits(f) == 0 {
254
				if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 {
255
					p.As = AXORPS
256
					p.From = p.To
257
					break
258
				}
259
			}
260
		}
261
		fallthrough
262

263
	case AFMOVD,
264
		AFADDD,
265
		AFSUBD,
266
		AFSUBRD,
267
		AFMULD,
268
		AFDIVD,
269
		AFDIVRD,
270
		AFCOMD,
271
		AFCOMDP,
272
		AADDSD,
273
		ASUBSD,
274
		AMULSD,
275
		ADIVSD,
276
		ACOMISD,
277
		AUCOMISD:
278
		if p.From.Type == obj.TYPE_FCONST {
279
			f64 := p.From.Val.(float64)
280
			p.From.Type = obj.TYPE_MEM
281
			p.From.Name = obj.NAME_EXTERN
282
			p.From.Sym = ctxt.Float64Sym(f64)
283
			p.From.Offset = 0
284
		}
285
	}
286

287
	if ctxt.Flag_dynlink {
288
		rewriteToUseGot(ctxt, p, newprog)
289
	}
290

291
	if ctxt.Flag_shared && ctxt.Arch.Family == sys.I386 {
292
		rewriteToPcrel(ctxt, p, newprog)
293
	}
294
}
295

296
// Rewrite p, if necessary, to access global data via the global offset table.
297
func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
298
	var lea, mov obj.As
299
	var reg int16
300
	if ctxt.Arch.Family == sys.AMD64 {
301
		lea = ALEAQ
302
		mov = AMOVQ
303
		reg = REG_R15
304
	} else {
305
		lea = ALEAL
306
		mov = AMOVL
307
		reg = REG_CX
308
		if p.As == ALEAL && p.To.Reg != p.From.Reg && p.To.Reg != p.From.Index {
309
			// Special case: clobber the destination register with
310
			// the PC so we don't have to clobber CX.
311
			// The SSA backend depends on CX not being clobbered across LEAL.
312
			// See cmd/compile/internal/ssa/gen/386.rules (search for Flag_shared).
313
			reg = p.To.Reg
314
		}
315
	}
316

317
	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
318
		//     ADUFFxxx $offset
319
		// becomes
320
		//     $MOV runtime.duffxxx@GOT, $reg
321
		//     $LEA $offset($reg), $reg
322
		//     CALL $reg
323
		// (we use LEAx rather than ADDx because ADDx clobbers
324
		// flags and duffzero on 386 does not otherwise do so).
325
		var sym *obj.LSym
326
		if p.As == obj.ADUFFZERO {
327
			sym = ctxt.Lookup("runtime.duffzero")
328
		} else {
329
			sym = ctxt.Lookup("runtime.duffcopy")
330
		}
331
		offset := p.To.Offset
332
		p.As = mov
333
		p.From.Type = obj.TYPE_MEM
334
		p.From.Name = obj.NAME_GOTREF
335
		p.From.Sym = sym
336
		p.To.Type = obj.TYPE_REG
337
		p.To.Reg = reg
338
		p.To.Offset = 0
339
		p.To.Sym = nil
340
		p1 := obj.Appendp(p, newprog)
341
		p1.As = lea
342
		p1.From.Type = obj.TYPE_MEM
343
		p1.From.Offset = offset
344
		p1.From.Reg = reg
345
		p1.To.Type = obj.TYPE_REG
346
		p1.To.Reg = reg
347
		p2 := obj.Appendp(p1, newprog)
348
		p2.As = obj.ACALL
349
		p2.To.Type = obj.TYPE_REG
350
		p2.To.Reg = reg
351
	}
352

353
	// We only care about global data: NAME_EXTERN means a global
354
	// symbol in the Go sense, and p.Sym.Local is true for a few
355
	// internally defined symbols.
356
	if p.As == lea && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
357
		// $LEA sym, Rx becomes $MOV $sym, Rx which will be rewritten below
358
		p.As = mov
359
		p.From.Type = obj.TYPE_ADDR
360
	}
361
	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
362
		// $MOV $sym, Rx becomes $MOV sym@GOT, Rx
363
		// $MOV $sym+<off>, Rx becomes $MOV sym@GOT, Rx; $LEA <off>(Rx), Rx
364
		// On 386 only, more complicated things like PUSHL $sym become $MOV sym@GOT, CX; PUSHL CX
365
		cmplxdest := false
366
		pAs := p.As
367
		var dest obj.Addr
368
		if p.To.Type != obj.TYPE_REG || pAs != mov {
369
			if ctxt.Arch.Family == sys.AMD64 {
370
				ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
371
			}
372
			cmplxdest = true
373
			dest = p.To
374
			p.As = mov
375
			p.To.Type = obj.TYPE_REG
376
			p.To.Reg = reg
377
			p.To.Sym = nil
378
			p.To.Name = obj.NAME_NONE
379
		}
380
		p.From.Type = obj.TYPE_MEM
381
		p.From.Name = obj.NAME_GOTREF
382
		q := p
383
		if p.From.Offset != 0 {
384
			q = obj.Appendp(p, newprog)
385
			q.As = lea
386
			q.From.Type = obj.TYPE_MEM
387
			q.From.Reg = p.To.Reg
388
			q.From.Offset = p.From.Offset
389
			q.To = p.To
390
			p.From.Offset = 0
391
		}
392
		if cmplxdest {
393
			q = obj.Appendp(q, newprog)
394
			q.As = pAs
395
			q.To = dest
396
			q.From.Type = obj.TYPE_REG
397
			q.From.Reg = reg
398
		}
399
	}
400
	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
401
		ctxt.Diag("don't know how to handle %v with -dynlink", p)
402
	}
403
	var source *obj.Addr
404
	// MOVx sym, Ry becomes $MOV sym@GOT, R15; MOVx (R15), Ry
405
	// MOVx Ry, sym becomes $MOV sym@GOT, R15; MOVx Ry, (R15)
406
	// An addition may be inserted between the two MOVs if there is an offset.
407
	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
408
		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
409
			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
410
		}
411
		source = &p.From
412
	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
413
		source = &p.To
414
	} else {
415
		return
416
	}
417
	if p.As == obj.ACALL {
418
		// When dynlinking on 386, almost any call might end up being a call
419
		// to a PLT, so make sure the GOT pointer is loaded into BX.
420
		// RegTo2 is set on the replacement call insn to stop it being
421
		// processed when it is in turn passed to progedit.
422
		//
423
		// We disable open-coded defers in buildssa() on 386 ONLY with shared
424
		// libraries because of this extra code added before deferreturn calls.
425
		if ctxt.Arch.Family == sys.AMD64 || (p.To.Sym != nil && p.To.Sym.Local()) || p.RegTo2 != 0 {
426
			return
427
		}
428
		p1 := obj.Appendp(p, newprog)
429
		p2 := obj.Appendp(p1, newprog)
430

431
		p1.As = ALEAL
432
		p1.From.Type = obj.TYPE_MEM
433
		p1.From.Name = obj.NAME_STATIC
434
		p1.From.Sym = ctxt.Lookup("_GLOBAL_OFFSET_TABLE_")
435
		p1.To.Type = obj.TYPE_REG
436
		p1.To.Reg = REG_BX
437

438
		p2.As = p.As
439
		p2.Scond = p.Scond
440
		p2.From = p.From
441
		if p.RestArgs != nil {
442
			p2.RestArgs = append(p2.RestArgs, p.RestArgs...)
443
		}
444
		p2.Reg = p.Reg
445
		p2.To = p.To
446
		// p.To.Type was set to TYPE_BRANCH above, but that makes checkaddr
447
		// in ../pass.go complain, so set it back to TYPE_MEM here, until p2
448
		// itself gets passed to progedit.
449
		p2.To.Type = obj.TYPE_MEM
450
		p2.RegTo2 = 1
451

452
		obj.Nopout(p)
453
		return
454

455
	}
456
	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ARET || p.As == obj.AJMP {
457
		return
458
	}
459
	if source.Type != obj.TYPE_MEM {
460
		ctxt.Diag("don't know how to handle %v with -dynlink", p)
461
	}
462
	p1 := obj.Appendp(p, newprog)
463
	p2 := obj.Appendp(p1, newprog)
464

465
	p1.As = mov
466
	p1.From.Type = obj.TYPE_MEM
467
	p1.From.Sym = source.Sym
468
	p1.From.Name = obj.NAME_GOTREF
469
	p1.To.Type = obj.TYPE_REG
470
	p1.To.Reg = reg
471

472
	p2.As = p.As
473
	p2.From = p.From
474
	p2.To = p.To
475
	if p.From.Name == obj.NAME_EXTERN {
476
		p2.From.Reg = reg
477
		p2.From.Name = obj.NAME_NONE
478
		p2.From.Sym = nil
479
	} else if p.To.Name == obj.NAME_EXTERN {
480
		p2.To.Reg = reg
481
		p2.To.Name = obj.NAME_NONE
482
		p2.To.Sym = nil
483
	} else {
484
		return
485
	}
486
	obj.Nopout(p)
487
}
488

489
func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
490
	// RegTo2 is set on the instructions we insert here so they don't get
491
	// processed twice.
492
	if p.RegTo2 != 0 {
493
		return
494
	}
495
	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
496
		return
497
	}
498
	// Any Prog (aside from the above special cases) with an Addr with Name ==
499
	// NAME_EXTERN, NAME_STATIC or NAME_GOTREF has a CALL __x86.get_pc_thunk.XX
500
	// inserted before it.
501
	isName := func(a *obj.Addr) bool {
502
		if a.Sym == nil || (a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR) || a.Reg != 0 {
503
			return false
504
		}
505
		if a.Sym.Type == objabi.STLSBSS {
506
			return false
507
		}
508
		return a.Name == obj.NAME_EXTERN || a.Name == obj.NAME_STATIC || a.Name == obj.NAME_GOTREF
509
	}
510

511
	if isName(&p.From) && p.From.Type == obj.TYPE_ADDR {
512
		// Handle things like "MOVL $sym, (SP)" or "PUSHL $sym" by rewriting
513
		// to "MOVL $sym, CX; MOVL CX, (SP)" or "MOVL $sym, CX; PUSHL CX"
514
		// respectively.
515
		if p.To.Type != obj.TYPE_REG {
516
			q := obj.Appendp(p, newprog)
517
			q.As = p.As
518
			q.From.Type = obj.TYPE_REG
519
			q.From.Reg = REG_CX
520
			q.To = p.To
521
			p.As = AMOVL
522
			p.To.Type = obj.TYPE_REG
523
			p.To.Reg = REG_CX
524
			p.To.Sym = nil
525
			p.To.Name = obj.NAME_NONE
526
		}
527
	}
528

529
	if !isName(&p.From) && !isName(&p.To) && (p.GetFrom3() == nil || !isName(p.GetFrom3())) {
530
		return
531
	}
532
	var dst int16 = REG_CX
533
	if (p.As == ALEAL || p.As == AMOVL) && p.To.Reg != p.From.Reg && p.To.Reg != p.From.Index {
534
		dst = p.To.Reg
535
		// Why? See the comment near the top of rewriteToUseGot above.
536
		// AMOVLs might be introduced by the GOT rewrites.
537
	}
538
	q := obj.Appendp(p, newprog)
539
	q.RegTo2 = 1
540
	r := obj.Appendp(q, newprog)
541
	r.RegTo2 = 1
542
	q.As = obj.ACALL
543
	thunkname := "__x86.get_pc_thunk." + strings.ToLower(rconv(int(dst)))
544
	q.To.Sym = ctxt.LookupInit(thunkname, func(s *obj.LSym) { s.Set(obj.AttrLocal, true) })
545
	q.To.Type = obj.TYPE_MEM
546
	q.To.Name = obj.NAME_EXTERN
547
	r.As = p.As
548
	r.Scond = p.Scond
549
	r.From = p.From
550
	r.RestArgs = p.RestArgs
551
	r.Reg = p.Reg
552
	r.To = p.To
553
	if isName(&p.From) {
554
		r.From.Reg = dst
555
	}
556
	if isName(&p.To) {
557
		r.To.Reg = dst
558
	}
559
	if p.GetFrom3() != nil && isName(p.GetFrom3()) {
560
		r.GetFrom3().Reg = dst
561
	}
562
	obj.Nopout(p)
563
}
564

565
func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
566
	if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
567
		return
568
	}
569

570
	p := cursym.Func.Text
571
	autoffset := int32(p.To.Offset)
572
	if autoffset < 0 {
573
		autoffset = 0
574
	}
575

576
	hasCall := false
577
	for q := p; q != nil; q = q.Link {
578
		if q.As == obj.ACALL || q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO {
579
			hasCall = true
580
			break
581
		}
582
	}
583

584
	var bpsize int
585
	if ctxt.Arch.Family == sys.AMD64 &&
586
		!p.From.Sym.NoFrame() && // (1) below
587
		!(autoffset == 0 && p.From.Sym.NoSplit()) && // (2) below
588
		!(autoffset == 0 && !hasCall) { // (3) below
589
		// Make room to save a base pointer.
590
		// There are 2 cases we must avoid:
591
		// 1) If noframe is set (which we do for functions which tail call).
592
		// 2) Scary runtime internals which would be all messed up by frame pointers.
593
		//    We detect these using a heuristic: frameless nosplit functions.
594
		//    TODO: Maybe someday we label them all with NOFRAME and get rid of this heuristic.
595
		// For performance, we also want to avoid:
596
		// 3) Frameless leaf functions
597
		bpsize = ctxt.Arch.PtrSize
598
		autoffset += int32(bpsize)
599
		p.To.Offset += int64(bpsize)
600
	} else {
601
		bpsize = 0
602
	}
603

604
	textarg := int64(p.To.Val.(int32))
605
	cursym.Func.Args = int32(textarg)
606
	cursym.Func.Locals = int32(p.To.Offset)
607

608
	// TODO(rsc): Remove.
609
	if ctxt.Arch.Family == sys.I386 && cursym.Func.Locals < 0 {
610
		cursym.Func.Locals = 0
611
	}
612

613
	// TODO(rsc): Remove 'ctxt.Arch.Family == sys.AMD64 &&'.
614
	if ctxt.Arch.Family == sys.AMD64 && autoffset < objabi.StackSmall && !p.From.Sym.NoSplit() {
615
		leaf := true
616
	LeafSearch:
617
		for q := p; q != nil; q = q.Link {
618
			switch q.As {
619
			case obj.ACALL:
620
				// Treat common runtime calls that take no arguments
621
				// the same as duffcopy and duffzero.
622
				if !isZeroArgRuntimeCall(q.To.Sym) {
623
					leaf = false
624
					break LeafSearch
625
				}
626
				fallthrough
627
			case obj.ADUFFCOPY, obj.ADUFFZERO:
628
				if autoffset >= objabi.StackSmall-8 {
629
					leaf = false
630
					break LeafSearch
631
				}
632
			}
633
		}
634

635
		if leaf {
636
			p.From.Sym.Set(obj.AttrNoSplit, true)
637
		}
638
	}
639

640
	if !p.From.Sym.NoSplit() || p.From.Sym.Wrapper() {
641
		p = obj.Appendp(p, newprog)
642
		p = load_g_cx(ctxt, p, newprog) // load g into CX
643
	}
644

645
	if !cursym.Func.Text.From.Sym.NoSplit() {
646
		p = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg)) // emit split check
647
	}
648

649
	// Delve debugger would like the next instruction to be noted as the end of the function prologue.
650
	// TODO: are there other cases (e.g., wrapper functions) that need marking?
651
	markedPrologue := false
652

653
	if autoffset != 0 {
654
		if autoffset%int32(ctxt.Arch.RegSize) != 0 {
655
			ctxt.Diag("unaligned stack size %d", autoffset)
656
		}
657
		p = obj.Appendp(p, newprog)
658
		p.As = AADJSP
659
		p.From.Type = obj.TYPE_CONST
660
		p.From.Offset = int64(autoffset)
661
		p.Spadj = autoffset
662
		p.Pos = p.Pos.WithXlogue(src.PosPrologueEnd)
663
		markedPrologue = true
664
	}
665

666
	if bpsize > 0 {
667
		// Save caller's BP
668
		p = obj.Appendp(p, newprog)
669

670
		p.As = AMOVQ
671
		p.From.Type = obj.TYPE_REG
672
		p.From.Reg = REG_BP
673
		p.To.Type = obj.TYPE_MEM
674
		p.To.Reg = REG_SP
675
		p.To.Scale = 1
676
		p.To.Offset = int64(autoffset) - int64(bpsize)
677
		if !markedPrologue {
678
			p.Pos = p.Pos.WithXlogue(src.PosPrologueEnd)
679
		}
680

681
		// Move current frame to BP
682
		p = obj.Appendp(p, newprog)
683

684
		p.As = ALEAQ
685
		p.From.Type = obj.TYPE_MEM
686
		p.From.Reg = REG_SP
687
		p.From.Scale = 1
688
		p.From.Offset = int64(autoffset) - int64(bpsize)
689
		p.To.Type = obj.TYPE_REG
690
		p.To.Reg = REG_BP
691
	}
692

693
	if cursym.Func.Text.From.Sym.Wrapper() {
694
		// if g._panic != nil && g._panic.argp == FP {
695
		//   g._panic.argp = bottom-of-frame
696
		// }
697
		//
698
		//	MOVQ g_panic(CX), BX
699
		//	TESTQ BX, BX
700
		//	JNE checkargp
701
		// end:
702
		//	NOP
703
		//  ... rest of function ...
704
		// checkargp:
705
		//	LEAQ (autoffset+8)(SP), DI
706
		//	CMPQ panic_argp(BX), DI
707
		//	JNE end
708
		//  MOVQ SP, panic_argp(BX)
709
		//  JMP end
710
		//
711
		// The NOP is needed to give the jumps somewhere to land.
712
		// It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes.
713
		//
714
		// The layout is chosen to help static branch prediction:
715
		// Both conditional jumps are unlikely, so they are arranged to be forward jumps.
716

717
		// MOVQ g_panic(CX), BX
718
		p = obj.Appendp(p, newprog)
719
		p.As = AMOVQ
720
		p.From.Type = obj.TYPE_MEM
721
		p.From.Reg = REG_CX
722
		p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // g_panic
723
		p.To.Type = obj.TYPE_REG
724
		p.To.Reg = REG_BX
725
		if ctxt.Arch.Family == sys.I386 {
726
			p.As = AMOVL
727
		}
728

729
		// TESTQ BX, BX
730
		p = obj.Appendp(p, newprog)
731
		p.As = ATESTQ
732
		p.From.Type = obj.TYPE_REG
733
		p.From.Reg = REG_BX
734
		p.To.Type = obj.TYPE_REG
735
		p.To.Reg = REG_BX
736
		if ctxt.Arch.Family == sys.I386 {
737
			p.As = ATESTL
738
		}
739

740
		// JNE checkargp (checkargp to be resolved later)
741
		jne := obj.Appendp(p, newprog)
742
		jne.As = AJNE
743
		jne.To.Type = obj.TYPE_BRANCH
744

745
		// end:
746
		//  NOP
747
		end := obj.Appendp(jne, newprog)
748
		end.As = obj.ANOP
749

750
		// Fast forward to end of function.
751
		var last *obj.Prog
752
		for last = end; last.Link != nil; last = last.Link {
753
		}
754

755
		// LEAQ (autoffset+8)(SP), DI
756
		p = obj.Appendp(last, newprog)
757
		p.As = ALEAQ
758
		p.From.Type = obj.TYPE_MEM
759
		p.From.Reg = REG_SP
760
		p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize)
761
		p.To.Type = obj.TYPE_REG
762
		p.To.Reg = REG_DI
763
		if ctxt.Arch.Family == sys.I386 {
764
			p.As = ALEAL
765
		}
766

767
		// Set jne branch target.
768
		jne.To.SetTarget(p)
769

770
		// CMPQ panic_argp(BX), DI
771
		p = obj.Appendp(p, newprog)
772
		p.As = ACMPQ
773
		p.From.Type = obj.TYPE_MEM
774
		p.From.Reg = REG_BX
775
		p.From.Offset = 0 // Panic.argp
776
		p.To.Type = obj.TYPE_REG
777
		p.To.Reg = REG_DI
778
		if ctxt.Arch.Family == sys.I386 {
779
			p.As = ACMPL
780
		}
781

782
		// JNE end
783
		p = obj.Appendp(p, newprog)
784
		p.As = AJNE
785
		p.To.Type = obj.TYPE_BRANCH
786
		p.To.SetTarget(end)
787

788
		// MOVQ SP, panic_argp(BX)
789
		p = obj.Appendp(p, newprog)
790
		p.As = AMOVQ
791
		p.From.Type = obj.TYPE_REG
792
		p.From.Reg = REG_SP
793
		p.To.Type = obj.TYPE_MEM
794
		p.To.Reg = REG_BX
795
		p.To.Offset = 0 // Panic.argp
796
		if ctxt.Arch.Family == sys.I386 {
797
			p.As = AMOVL
798
		}
799

800
		// JMP end
801
		p = obj.Appendp(p, newprog)
802
		p.As = obj.AJMP
803
		p.To.Type = obj.TYPE_BRANCH
804
		p.To.SetTarget(end)
805

806
		// Reset p for following code.
807
		p = end
808
	}
809

810
	var deltasp int32
811
	for p = cursym.Func.Text; p != nil; p = p.Link {
812
		pcsize := ctxt.Arch.RegSize
813
		switch p.From.Name {
814
		case obj.NAME_AUTO:
815
			p.From.Offset += int64(deltasp) - int64(bpsize)
816
		case obj.NAME_PARAM:
817
			p.From.Offset += int64(deltasp) + int64(pcsize)
818
		}
819
		if p.GetFrom3() != nil {
820
			switch p.GetFrom3().Name {
821
			case obj.NAME_AUTO:
822
				p.GetFrom3().Offset += int64(deltasp) - int64(bpsize)
823
			case obj.NAME_PARAM:
824
				p.GetFrom3().Offset += int64(deltasp) + int64(pcsize)
825
			}
826
		}
827
		switch p.To.Name {
828
		case obj.NAME_AUTO:
829
			p.To.Offset += int64(deltasp) - int64(bpsize)
830
		case obj.NAME_PARAM:
831
			p.To.Offset += int64(deltasp) + int64(pcsize)
832
		}
833

834
		switch p.As {
835
		default:
836
			continue
837

838
		case APUSHL, APUSHFL:
839
			deltasp += 4
840
			p.Spadj = 4
841
			continue
842

843
		case APUSHQ, APUSHFQ:
844
			deltasp += 8
845
			p.Spadj = 8
846
			continue
847

848
		case APUSHW, APUSHFW:
849
			deltasp += 2
850
			p.Spadj = 2
851
			continue
852

853
		case APOPL, APOPFL:
854
			deltasp -= 4
855
			p.Spadj = -4
856
			continue
857

858
		case APOPQ, APOPFQ:
859
			deltasp -= 8
860
			p.Spadj = -8
861
			continue
862

863
		case APOPW, APOPFW:
864
			deltasp -= 2
865
			p.Spadj = -2
866
			continue
867

868
		case AADJSP:
869
			p.Spadj = int32(p.From.Offset)
870
			deltasp += int32(p.From.Offset)
871
			continue
872

873
		case obj.ARET:
874
			// do nothing
875
		}
876

877
		if autoffset != deltasp {
878
			ctxt.Diag("unbalanced PUSH/POP")
879
		}
880

881
		if autoffset != 0 {
882
			to := p.To // Keep To attached to RET for retjmp below
883
			p.To = obj.Addr{}
884
			if bpsize > 0 {
885
				// Restore caller's BP
886
				p.As = AMOVQ
887

888
				p.From.Type = obj.TYPE_MEM
889
				p.From.Reg = REG_SP
890
				p.From.Scale = 1
891
				p.From.Offset = int64(autoffset) - int64(bpsize)
892
				p.To.Type = obj.TYPE_REG
893
				p.To.Reg = REG_BP
894
				p = obj.Appendp(p, newprog)
895
			}
896

897
			p.As = AADJSP
898
			p.From.Type = obj.TYPE_CONST
899
			p.From.Offset = int64(-autoffset)
900
			p.Spadj = -autoffset
901
			p = obj.Appendp(p, newprog)
902
			p.As = obj.ARET
903
			p.To = to
904

905
			// If there are instructions following
906
			// this ARET, they come from a branch
907
			// with the same stackframe, so undo
908
			// the cleanup.
909
			p.Spadj = +autoffset
910
		}
911

912
		if p.To.Sym != nil { // retjmp
913
			p.As = obj.AJMP
914
		}
915
	}
916
}
917

918
func isZeroArgRuntimeCall(s *obj.LSym) bool {
919
	if s == nil {
920
		return false
921
	}
922
	switch s.Name {
923
	case "runtime.panicdivide", "runtime.panicwrap", "runtime.panicshift":
924
		return true
925
	}
926
	if strings.HasPrefix(s.Name, "runtime.panicIndex") || strings.HasPrefix(s.Name, "runtime.panicSlice") {
927
		// These functions do take arguments (in registers),
928
		// but use no stack before they do a stack check. We
929
		// should include them. See issue 31219.
930
		return true
931
	}
932
	return false
933
}
934

935
func indir_cx(ctxt *obj.Link, a *obj.Addr) {
936
	a.Type = obj.TYPE_MEM
937
	a.Reg = REG_CX
938
}
939

940
// Append code to p to load g into cx.
941
// Overwrites p with the first instruction (no first appendp).
942
// Overwriting p is unusual but it lets use this in both the
943
// prologue (caller must call appendp first) and in the epilogue.
944
// Returns last new instruction.
945
func load_g_cx(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) *obj.Prog {
946
	p.As = AMOVQ
947
	if ctxt.Arch.PtrSize == 4 {
948
		p.As = AMOVL
949
	}
950
	p.From.Type = obj.TYPE_MEM
951
	p.From.Reg = REG_TLS
952
	p.From.Offset = 0
953
	p.To.Type = obj.TYPE_REG
954
	p.To.Reg = REG_CX
955

956
	next := p.Link
957
	progedit(ctxt, p, newprog)
958
	for p.Link != next {
959
		p = p.Link
960
		progedit(ctxt, p, newprog)
961
	}
962

963
	if p.From.Index == REG_TLS {
964
		p.From.Scale = 2
965
	}
966

967
	return p
968
}
969

970
// Append code to p to check for stack split.
971
// Appends to (does not overwrite) p.
972
// Assumes g is in CX.
973
// Returns last new instruction.
974
func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc, framesize int32, textarg int32) *obj.Prog {
975
	cmp := ACMPQ
976
	lea := ALEAQ
977
	mov := AMOVQ
978
	sub := ASUBQ
979

980
	if ctxt.Arch.Family == sys.I386 {
981
		cmp = ACMPL
982
		lea = ALEAL
983
		mov = AMOVL
984
		sub = ASUBL
985
	}
986

987
	var q1 *obj.Prog
988
	if framesize <= objabi.StackSmall {
989
		// small stack: SP <= stackguard
990
		//	CMPQ SP, stackguard
991
		p = obj.Appendp(p, newprog)
992

993
		p.As = cmp
994
		p.From.Type = obj.TYPE_REG
995
		p.From.Reg = REG_SP
996
		indir_cx(ctxt, &p.To)
997
		p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
998
		if cursym.CFunc() {
999
			p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
1000
		}
1001

1002
		// Mark the stack bound check and morestack call async nonpreemptible.
1003
		// If we get preempted here, when resumed the preemption request is
1004
		// cleared, but we'll still call morestack, which will double the stack
1005
		// unnecessarily. See issue #35470.
1006
		p = ctxt.StartUnsafePoint(p, newprog)
1007
	} else if framesize <= objabi.StackBig {
1008
		// large stack: SP-framesize <= stackguard-StackSmall
1009
		//	LEAQ -xxx(SP), AX
1010
		//	CMPQ AX, stackguard
1011
		p = obj.Appendp(p, newprog)
1012

1013
		p.As = lea
1014
		p.From.Type = obj.TYPE_MEM
1015
		p.From.Reg = REG_SP
1016
		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
1017
		p.To.Type = obj.TYPE_REG
1018
		p.To.Reg = REG_AX
1019

1020
		p = obj.Appendp(p, newprog)
1021
		p.As = cmp
1022
		p.From.Type = obj.TYPE_REG
1023
		p.From.Reg = REG_AX
1024
		indir_cx(ctxt, &p.To)
1025
		p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
1026
		if cursym.CFunc() {
1027
			p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
1028
		}
1029

1030
		p = ctxt.StartUnsafePoint(p, newprog) // see the comment above
1031
	} else {
1032
		// Such a large stack we need to protect against wraparound.
1033
		// If SP is close to zero:
1034
		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
1035
		// The +StackGuard on both sides is required to keep the left side positive:
1036
		// SP is allowed to be slightly below stackguard. See stack.h.
1037
		//
1038
		// Preemption sets stackguard to StackPreempt, a very large value.
1039
		// That breaks the math above, so we have to check for that explicitly.
1040
		//	MOVQ	stackguard, SI
1041
		//	CMPQ	SI, $StackPreempt
1042
		//	JEQ	label-of-call-to-morestack
1043
		//	LEAQ	StackGuard(SP), AX
1044
		//	SUBQ	SI, AX
1045
		//	CMPQ	AX, $(framesize+(StackGuard-StackSmall))
1046

1047
		p = obj.Appendp(p, newprog)
1048

1049
		p.As = mov
1050
		indir_cx(ctxt, &p.From)
1051
		p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
1052
		if cursym.CFunc() {
1053
			p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
1054
		}
1055
		p.To.Type = obj.TYPE_REG
1056
		p.To.Reg = REG_SI
1057

1058
		p = ctxt.StartUnsafePoint(p, newprog) // see the comment above
1059

1060
		p = obj.Appendp(p, newprog)
1061
		p.As = cmp
1062
		p.From.Type = obj.TYPE_REG
1063
		p.From.Reg = REG_SI
1064
		p.To.Type = obj.TYPE_CONST
1065
		p.To.Offset = objabi.StackPreempt
1066
		if ctxt.Arch.Family == sys.I386 {
1067
			p.To.Offset = int64(uint32(objabi.StackPreempt & (1<<32 - 1)))
1068
		}
1069

1070
		p = obj.Appendp(p, newprog)
1071
		p.As = AJEQ
1072
		p.To.Type = obj.TYPE_BRANCH
1073
		q1 = p
1074

1075
		p = obj.Appendp(p, newprog)
1076
		p.As = lea
1077
		p.From.Type = obj.TYPE_MEM
1078
		p.From.Reg = REG_SP
1079
		p.From.Offset = int64(objabi.StackGuard)
1080
		p.To.Type = obj.TYPE_REG
1081
		p.To.Reg = REG_AX
1082

1083
		p = obj.Appendp(p, newprog)
1084
		p.As = sub
1085
		p.From.Type = obj.TYPE_REG
1086
		p.From.Reg = REG_SI
1087
		p.To.Type = obj.TYPE_REG
1088
		p.To.Reg = REG_AX
1089

1090
		p = obj.Appendp(p, newprog)
1091
		p.As = cmp
1092
		p.From.Type = obj.TYPE_REG
1093
		p.From.Reg = REG_AX
1094
		p.To.Type = obj.TYPE_CONST
1095
		p.To.Offset = int64(framesize) + (int64(objabi.StackGuard) - objabi.StackSmall)
1096
	}
1097

1098
	// common
1099
	jls := obj.Appendp(p, newprog)
1100
	jls.As = AJLS
1101
	jls.To.Type = obj.TYPE_BRANCH
1102

1103
	end := ctxt.EndUnsafePoint(jls, newprog, -1)
1104

1105
	var last *obj.Prog
1106
	for last = cursym.Func.Text; last.Link != nil; last = last.Link {
1107
	}
1108

1109
	// Now we are at the end of the function, but logically
1110
	// we are still in function prologue. We need to fix the
1111
	// SP data and PCDATA.
1112
	spfix := obj.Appendp(last, newprog)
1113
	spfix.As = obj.ANOP
1114
	spfix.Spadj = -framesize
1115

1116
	pcdata := ctxt.EmitEntryStackMap(cursym, spfix, newprog)
1117
	pcdata = ctxt.StartUnsafePoint(pcdata, newprog)
1118

1119
	call := obj.Appendp(pcdata, newprog)
1120
	call.Pos = cursym.Func.Text.Pos
1121
	call.As = obj.ACALL
1122
	call.To.Type = obj.TYPE_BRANCH
1123
	call.To.Name = obj.NAME_EXTERN
1124
	morestack := "runtime.morestack"
1125
	switch {
1126
	case cursym.CFunc():
1127
		morestack = "runtime.morestackc"
1128
	case !cursym.Func.Text.From.Sym.NeedCtxt():
1129
		morestack = "runtime.morestack_noctxt"
1130
	}
1131
	call.To.Sym = ctxt.Lookup(morestack)
1132
	// When compiling 386 code for dynamic linking, the call needs to be adjusted
1133
	// to follow PIC rules. This in turn can insert more instructions, so we need
1134
	// to keep track of the start of the call (where the jump will be to) and the
1135
	// end (which following instructions are appended to).
1136
	callend := call
1137
	progedit(ctxt, callend, newprog)
1138
	for ; callend.Link != nil; callend = callend.Link {
1139
		progedit(ctxt, callend.Link, newprog)
1140
	}
1141

1142
	pcdata = ctxt.EndUnsafePoint(callend, newprog, -1)
1143

1144
	jmp := obj.Appendp(pcdata, newprog)
1145
	jmp.As = obj.AJMP
1146
	jmp.To.Type = obj.TYPE_BRANCH
1147
	jmp.To.SetTarget(cursym.Func.Text.Link)
1148
	jmp.Spadj = +framesize
1149

1150
	jls.To.SetTarget(call)
1151
	if q1 != nil {
1152
		q1.To.SetTarget(call)
1153
	}
1154

1155
	return end
1156
}
1157

1158
var unaryDst = map[obj.As]bool{
1159
	ABSWAPL:     true,
1160
	ABSWAPQ:     true,
1161
	ACLDEMOTE:   true,
1162
	ACLFLUSH:    true,
1163
	ACLFLUSHOPT: true,
1164
	ACLWB:       true,
1165
	ACMPXCHG16B: true,
1166
	ACMPXCHG8B:  true,
1167
	ADECB:       true,
1168
	ADECL:       true,
1169
	ADECQ:       true,
1170
	ADECW:       true,
1171
	AFBSTP:      true,
1172
	AFFREE:      true,
1173
	AFLDENV:     true,
1174
	AFSAVE:      true,
1175
	AFSTCW:      true,
1176
	AFSTENV:     true,
1177
	AFSTSW:      true,
1178
	AFXSAVE64:   true,
1179
	AFXSAVE:     true,
1180
	AINCB:       true,
1181
	AINCL:       true,
1182
	AINCQ:       true,
1183
	AINCW:       true,
1184
	ANEGB:       true,
1185
	ANEGL:       true,
1186
	ANEGQ:       true,
1187
	ANEGW:       true,
1188
	ANOTB:       true,
1189
	ANOTL:       true,
1190
	ANOTQ:       true,
1191
	ANOTW:       true,
1192
	APOPL:       true,
1193
	APOPQ:       true,
1194
	APOPW:       true,
1195
	ARDFSBASEL:  true,
1196
	ARDFSBASEQ:  true,
1197
	ARDGSBASEL:  true,
1198
	ARDGSBASEQ:  true,
1199
	ARDRANDL:    true,
1200
	ARDRANDQ:    true,
1201
	ARDRANDW:    true,
1202
	ARDSEEDL:    true,
1203
	ARDSEEDQ:    true,
1204
	ARDSEEDW:    true,
1205
	ASETCC:      true,
1206
	ASETCS:      true,
1207
	ASETEQ:      true,
1208
	ASETGE:      true,
1209
	ASETGT:      true,
1210
	ASETHI:      true,
1211
	ASETLE:      true,
1212
	ASETLS:      true,
1213
	ASETLT:      true,
1214
	ASETMI:      true,
1215
	ASETNE:      true,
1216
	ASETOC:      true,
1217
	ASETOS:      true,
1218
	ASETPC:      true,
1219
	ASETPL:      true,
1220
	ASETPS:      true,
1221
	ASGDT:       true,
1222
	ASIDT:       true,
1223
	ASLDTL:      true,
1224
	ASLDTQ:      true,
1225
	ASLDTW:      true,
1226
	ASMSWL:      true,
1227
	ASMSWQ:      true,
1228
	ASMSWW:      true,
1229
	ASTMXCSR:    true,
1230
	ASTRL:       true,
1231
	ASTRQ:       true,
1232
	ASTRW:       true,
1233
	AXSAVE64:    true,
1234
	AXSAVE:      true,
1235
	AXSAVEC64:   true,
1236
	AXSAVEC:     true,
1237
	AXSAVEOPT64: true,
1238
	AXSAVEOPT:   true,
1239
	AXSAVES64:   true,
1240
	AXSAVES:     true,
1241
}
1242

1243
var Linkamd64 = obj.LinkArch{
1244
	Arch:           sys.ArchAMD64,
1245
	Init:           instinit,
1246
	Preprocess:     preprocess,
1247
	Assemble:       span6,
1248
	Progedit:       progedit,
1249
	UnaryDst:       unaryDst,
1250
	DWARFRegisters: AMD64DWARFRegisters,
1251
}
1252

1253
var Link386 = obj.LinkArch{
1254
	Arch:           sys.Arch386,
1255
	Init:           instinit,
1256
	Preprocess:     preprocess,
1257
	Assemble:       span6,
1258
	Progedit:       progedit,
1259
	UnaryDst:       unaryDst,
1260
	DWARFRegisters: X86DWARFRegisters,
1261
}
1262

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.