1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package obj
6
7import (
8	"bytes"
9	"cmd/internal/objabi"
10	"fmt"
11	"internal/abi"
12	"internal/buildcfg"
13	"io"
14	"strings"
15)
16
17const REG_NONE = 0
18
19// Line returns a string containing the filename and line number for p
20func (p *Prog) Line() string {
21	return p.Ctxt.OutermostPos(p.Pos).Format(false, true)
22}
23func (p *Prog) InnermostLine(w io.Writer) {
24	p.Ctxt.InnermostPos(p.Pos).WriteTo(w, false, true)
25}
26
27// InnermostLineNumber returns a string containing the line number for the
28// innermost inlined function (if any inlining) at p's position
29func (p *Prog) InnermostLineNumber() string {
30	return p.Ctxt.InnermostPos(p.Pos).LineNumber()
31}
32
33// InnermostLineNumberHTML returns a string containing the line number for the
34// innermost inlined function (if any inlining) at p's position
35func (p *Prog) InnermostLineNumberHTML() string {
36	return p.Ctxt.InnermostPos(p.Pos).LineNumberHTML()
37}
38
39// InnermostFilename returns a string containing the innermost
40// (in inlining) filename at p's position
41func (p *Prog) InnermostFilename() string {
42	// TODO For now, this is only used for debugging output, and if we need more/better information, it might change.
43	// An example of what we might want to see is the full stack of positions for inlined code, so we get some visibility into what is recorded there.
44	pos := p.Ctxt.InnermostPos(p.Pos)
45	if !pos.IsKnown() {
46		return "<unknown file name>"
47	}
48	return pos.Filename()
49}
50
51var armCondCode = []string{
52	".EQ",
53	".NE",
54	".CS",
55	".CC",
56	".MI",
57	".PL",
58	".VS",
59	".VC",
60	".HI",
61	".LS",
62	".GE",
63	".LT",
64	".GT",
65	".LE",
66	"",
67	".NV",
68}
69
70/* ARM scond byte */
71const (
72	C_SCOND     = (1 << 4) - 1
73	C_SBIT      = 1 << 4
74	C_PBIT      = 1 << 5
75	C_WBIT      = 1 << 6
76	C_FBIT      = 1 << 7
77	C_UBIT      = 1 << 7
78	C_SCOND_XOR = 14
79)
80
81// CConv formats opcode suffix bits (Prog.Scond).
82func CConv(s uint8) string {
83	if s == 0 {
84		return ""
85	}
86	for i := range opSuffixSpace {
87		sset := &opSuffixSpace[i]
88		if sset.arch == buildcfg.GOARCH {
89			return sset.cconv(s)
90		}
91	}
92	return fmt.Sprintf("SC???%d", s)
93}
94
95// CConvARM formats ARM opcode suffix bits (mostly condition codes).
96func CConvARM(s uint8) string {
97	// TODO: could be great to move suffix-related things into
98	// ARM asm backends some day.
99	// obj/x86 can be used as an example.
100
101	sc := armCondCode[(s&C_SCOND)^C_SCOND_XOR]
102	if s&C_SBIT != 0 {
103		sc += ".S"
104	}
105	if s&C_PBIT != 0 {
106		sc += ".P"
107	}
108	if s&C_WBIT != 0 {
109		sc += ".W"
110	}
111	if s&C_UBIT != 0 { /* ambiguous with FBIT */
112		sc += ".U"
113	}
114	return sc
115}
116
117func (p *Prog) String() string {
118	if p == nil {
119		return "<nil Prog>"
120	}
121	if p.Ctxt == nil {
122		return "<Prog without ctxt>"
123	}
124	return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.Line(), p.InstructionString())
125}
126
127func (p *Prog) InnermostString(w io.Writer) {
128	if p == nil {
129		io.WriteString(w, "<nil Prog>")
130		return
131	}
132	if p.Ctxt == nil {
133		io.WriteString(w, "<Prog without ctxt>")
134		return
135	}
136	fmt.Fprintf(w, "%.5d (", p.Pc)
137	p.InnermostLine(w)
138	io.WriteString(w, ")\t")
139	p.WriteInstructionString(w)
140}
141
142// InstructionString returns a string representation of the instruction without preceding
143// program counter or file and line number.
144func (p *Prog) InstructionString() string {
145	buf := new(bytes.Buffer)
146	p.WriteInstructionString(buf)
147	return buf.String()
148}
149
150// WriteInstructionString writes a string representation of the instruction without preceding
151// program counter or file and line number.
152func (p *Prog) WriteInstructionString(w io.Writer) {
153	if p == nil {
154		io.WriteString(w, "<nil Prog>")
155		return
156	}
157
158	if p.Ctxt == nil {
159		io.WriteString(w, "<Prog without ctxt>")
160		return
161	}
162
163	sc := CConv(p.Scond)
164
165	io.WriteString(w, p.As.String())
166	io.WriteString(w, sc)
167	sep := "\t"
168
169	if p.From.Type != TYPE_NONE {
170		io.WriteString(w, sep)
171		WriteDconv(w, p, &p.From)
172		sep = ", "
173	}
174	if p.Reg != REG_NONE {
175		// Should not happen but might as well show it if it does.
176		fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.Reg)))
177		sep = ", "
178	}
179	for i := range p.RestArgs {
180		if p.RestArgs[i].Pos == Source {
181			io.WriteString(w, sep)
182			WriteDconv(w, p, &p.RestArgs[i].Addr)
183			sep = ", "
184		}
185	}
186
187	if p.As == ATEXT {
188		// If there are attributes, print them. Otherwise, skip the comma.
189		// In short, print one of these two:
190		// TEXT	foo(SB), DUPOK|NOSPLIT, $0
191		// TEXT	foo(SB), $0
192		s := p.From.Sym.TextAttrString()
193		if s != "" {
194			fmt.Fprintf(w, "%s%s", sep, s)
195			sep = ", "
196		}
197	}
198	if p.To.Type != TYPE_NONE {
199		io.WriteString(w, sep)
200		WriteDconv(w, p, &p.To)
201		sep = ", "
202	}
203	if p.RegTo2 != REG_NONE {
204		fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.RegTo2)))
205	}
206	for i := range p.RestArgs {
207		if p.RestArgs[i].Pos == Destination {
208			io.WriteString(w, sep)
209			WriteDconv(w, p, &p.RestArgs[i].Addr)
210			sep = ", "
211		}
212	}
213}
214
215func (ctxt *Link) NewProg() *Prog {
216	p := new(Prog)
217	p.Ctxt = ctxt
218	return p
219}
220
221func (ctxt *Link) CanReuseProgs() bool {
222	return ctxt.Debugasm == 0
223}
224
225// Dconv accepts an argument 'a' within a prog 'p' and returns a string
226// with a formatted version of the argument.
227func Dconv(p *Prog, a *Addr) string {
228	buf := new(bytes.Buffer)
229	writeDconv(buf, p, a, false)
230	return buf.String()
231}
232
233// DconvWithABIDetail accepts an argument 'a' within a prog 'p'
234// and returns a string with a formatted version of the argument, in
235// which text symbols are rendered with explicit ABI selectors.
236func DconvWithABIDetail(p *Prog, a *Addr) string {
237	buf := new(bytes.Buffer)
238	writeDconv(buf, p, a, true)
239	return buf.String()
240}
241
242// WriteDconv accepts an argument 'a' within a prog 'p'
243// and writes a formatted version of the arg to the writer.
244func WriteDconv(w io.Writer, p *Prog, a *Addr) {
245	writeDconv(w, p, a, false)
246}
247
248func writeDconv(w io.Writer, p *Prog, a *Addr, abiDetail bool) {
249	switch a.Type {
250	default:
251		fmt.Fprintf(w, "type=%d", a.Type)
252
253	case TYPE_NONE:
254		if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil {
255			a.WriteNameTo(w)
256			fmt.Fprintf(w, "(%v)(NONE)", Rconv(int(a.Reg)))
257		}
258
259	case TYPE_REG:
260		// TODO(rsc): This special case is for x86 instructions like
261		//	PINSRQ	CX,$1,X6
262		// where the $1 is included in the p->to Addr.
263		// Move into a new field.
264		if a.Offset != 0 && (a.Reg < RBaseARM64 || a.Reg >= RBaseMIPS) {
265			fmt.Fprintf(w, "$%d,%v", a.Offset, Rconv(int(a.Reg)))
266			return
267		}
268
269		if a.Name != NAME_NONE || a.Sym != nil {
270			a.WriteNameTo(w)
271			fmt.Fprintf(w, "(%v)(REG)", Rconv(int(a.Reg)))
272		} else {
273			io.WriteString(w, Rconv(int(a.Reg)))
274		}
275		if (RBaseARM64+1<<10+1<<9) /* arm64.REG_ELEM */ <= a.Reg &&
276			a.Reg < (RBaseARM64+1<<11) /* arm64.REG_ELEM_END */ {
277			fmt.Fprintf(w, "[%d]", a.Index)
278		}
279
280	case TYPE_BRANCH:
281		if a.Sym != nil {
282			fmt.Fprintf(w, "%s%s(SB)", a.Sym.Name, abiDecorate(a, abiDetail))
283		} else if a.Target() != nil {
284			fmt.Fprint(w, a.Target().Pc)
285		} else {
286			fmt.Fprintf(w, "%d(PC)", a.Offset)
287		}
288
289	case TYPE_INDIR:
290		io.WriteString(w, "*")
291		a.writeNameTo(w, abiDetail)
292
293	case TYPE_MEM:
294		a.WriteNameTo(w)
295		if a.Index != REG_NONE {
296			if a.Scale == 0 {
297				// arm64 shifted or extended register offset, scale = 0.
298				fmt.Fprintf(w, "(%v)", Rconv(int(a.Index)))
299			} else {
300				fmt.Fprintf(w, "(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
301			}
302		}
303
304	case TYPE_CONST:
305		io.WriteString(w, "$")
306		a.WriteNameTo(w)
307		if a.Reg != 0 {
308			fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
309		}
310
311	case TYPE_TEXTSIZE:
312		if a.Val.(int32) == abi.ArgsSizeUnknown {
313			fmt.Fprintf(w, "$%d", a.Offset)
314		} else {
315			fmt.Fprintf(w, "$%d-%d", a.Offset, a.Val.(int32))
316		}
317
318	case TYPE_FCONST:
319		str := fmt.Sprintf("%.17g", a.Val.(float64))
320		// Make sure 1 prints as 1.0
321		if !strings.ContainsAny(str, ".e") {
322			str += ".0"
323		}
324		fmt.Fprintf(w, "$(%s)", str)
325
326	case TYPE_SCONST:
327		fmt.Fprintf(w, "$%q", a.Val.(string))
328
329	case TYPE_ADDR:
330		io.WriteString(w, "$")
331		a.writeNameTo(w, abiDetail)
332
333	case TYPE_SHIFT:
334		v := int(a.Offset)
335		ops := "<<>>->@>"
336		switch buildcfg.GOARCH {
337		case "arm":
338			op := ops[((v>>5)&3)<<1:]
339			if v&(1<<4) != 0 {
340				fmt.Fprintf(w, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
341			} else {
342				fmt.Fprintf(w, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
343			}
344			if a.Reg != 0 {
345				fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
346			}
347		case "arm64":
348			op := ops[((v>>22)&3)<<1:]
349			r := (v >> 16) & 31
350			fmt.Fprintf(w, "%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63)
351		default:
352			panic("TYPE_SHIFT is not supported on " + buildcfg.GOARCH)
353		}
354
355	case TYPE_REGREG:
356		fmt.Fprintf(w, "(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
357
358	case TYPE_REGREG2:
359		fmt.Fprintf(w, "%v, %v", Rconv(int(a.Offset)), Rconv(int(a.Reg)))
360
361	case TYPE_REGLIST:
362		io.WriteString(w, RLconv(a.Offset))
363
364	case TYPE_SPECIAL:
365		io.WriteString(w, SPCconv(a.Offset))
366	}
367}
368
369func (a *Addr) WriteNameTo(w io.Writer) {
370	a.writeNameTo(w, false)
371}
372
373func (a *Addr) writeNameTo(w io.Writer, abiDetail bool) {
374
375	switch a.Name {
376	default:
377		fmt.Fprintf(w, "name=%d", a.Name)
378
379	case NAME_NONE:
380		switch {
381		case a.Reg == REG_NONE:
382			fmt.Fprint(w, a.Offset)
383		case a.Offset == 0:
384			fmt.Fprintf(w, "(%v)", Rconv(int(a.Reg)))
385		case a.Offset != 0:
386			fmt.Fprintf(w, "%d(%v)", a.Offset, Rconv(int(a.Reg)))
387		}
388
389		// Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type.
390	case NAME_EXTERN:
391		reg := "SB"
392		if a.Reg != REG_NONE {
393			reg = Rconv(int(a.Reg))
394		}
395		if a.Sym != nil {
396			fmt.Fprintf(w, "%s%s%s(%s)", a.Sym.Name, abiDecorate(a, abiDetail), offConv(a.Offset), reg)
397		} else {
398			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
399		}
400
401	case NAME_GOTREF:
402		reg := "SB"
403		if a.Reg != REG_NONE {
404			reg = Rconv(int(a.Reg))
405		}
406		if a.Sym != nil {
407			fmt.Fprintf(w, "%s%s@GOT(%s)", a.Sym.Name, offConv(a.Offset), reg)
408		} else {
409			fmt.Fprintf(w, "%s@GOT(%s)", offConv(a.Offset), reg)
410		}
411
412	case NAME_STATIC:
413		reg := "SB"
414		if a.Reg != REG_NONE {
415			reg = Rconv(int(a.Reg))
416		}
417		if a.Sym != nil {
418			fmt.Fprintf(w, "%s<>%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
419		} else {
420			fmt.Fprintf(w, "<>%s(%s)", offConv(a.Offset), reg)
421		}
422
423	case NAME_AUTO:
424		reg := "SP"
425		if a.Reg != REG_NONE {
426			reg = Rconv(int(a.Reg))
427		}
428		if a.Sym != nil {
429			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
430		} else {
431			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
432		}
433
434	case NAME_PARAM:
435		reg := "FP"
436		if a.Reg != REG_NONE {
437			reg = Rconv(int(a.Reg))
438		}
439		if a.Sym != nil {
440			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
441		} else {
442			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
443		}
444	case NAME_TOCREF:
445		reg := "SB"
446		if a.Reg != REG_NONE {
447			reg = Rconv(int(a.Reg))
448		}
449		if a.Sym != nil {
450			fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
451		} else {
452			fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
453		}
454	}
455}
456
457func offConv(off int64) string {
458	if off == 0 {
459		return ""
460	}
461	return fmt.Sprintf("%+d", off)
462}
463
464// opSuffixSet is like regListSet, but for opcode suffixes.
465//
466// Unlike some other similar structures, uint8 space is not
467// divided by its own values set (because there are only 256 of them).
468// Instead, every arch may interpret/format all 8 bits as they like,
469// as long as they register proper cconv function for it.
470type opSuffixSet struct {
471	arch  string
472	cconv func(suffix uint8) string
473}
474
475var opSuffixSpace []opSuffixSet
476
477// RegisterOpSuffix assigns cconv function for formatting opcode suffixes
478// when compiling for GOARCH=arch.
479//
480// cconv is never called with 0 argument.
481func RegisterOpSuffix(arch string, cconv func(uint8) string) {
482	opSuffixSpace = append(opSuffixSpace, opSuffixSet{
483		arch:  arch,
484		cconv: cconv,
485	})
486}
487
488type regSet struct {
489	lo    int
490	hi    int
491	Rconv func(int) string
492}
493
494// Few enough architectures that a linear scan is fastest.
495// Not even worth sorting.
496var regSpace []regSet
497
498/*
499	Each architecture defines a register space as a unique
500	integer range.
501	Here is the list of architectures and the base of their register spaces.
502*/
503
504const (
505	// Because of masking operations in the encodings, each register
506	// space should start at 0 modulo some power of 2.
507	RBase386     = 1 * 1024
508	RBaseAMD64   = 2 * 1024
509	RBaseARM     = 3 * 1024
510	RBasePPC64   = 4 * 1024  // range [4k, 8k)
511	RBaseARM64   = 8 * 1024  // range [8k, 13k)
512	RBaseMIPS    = 13 * 1024 // range [13k, 14k)
513	RBaseS390X   = 14 * 1024 // range [14k, 15k)
514	RBaseRISCV   = 15 * 1024 // range [15k, 16k)
515	RBaseWasm    = 16 * 1024
516	RBaseLOONG64 = 17 * 1024
517)
518
519// RegisterRegister binds a pretty-printer (Rconv) for register
520// numbers to a given register number range. Lo is inclusive,
521// hi exclusive (valid registers are lo through hi-1).
522func RegisterRegister(lo, hi int, Rconv func(int) string) {
523	regSpace = append(regSpace, regSet{lo, hi, Rconv})
524}
525
526func Rconv(reg int) string {
527	if reg == REG_NONE {
528		return "NONE"
529	}
530	for i := range regSpace {
531		rs := &regSpace[i]
532		if rs.lo <= reg && reg < rs.hi {
533			return rs.Rconv(reg)
534		}
535	}
536	return fmt.Sprintf("R???%d", reg)
537}
538
539type regListSet struct {
540	lo     int64
541	hi     int64
542	RLconv func(int64) string
543}
544
545var regListSpace []regListSet
546
547// Each architecture is allotted a distinct subspace: [Lo, Hi) for declaring its
548// arch-specific register list numbers.
549const (
550	RegListARMLo = 0
551	RegListARMHi = 1 << 16
552
553	// arm64 uses the 60th bit to differentiate from other archs
554	RegListARM64Lo = 1 << 60
555	RegListARM64Hi = 1<<61 - 1
556
557	// x86 uses the 61th bit to differentiate from other archs
558	RegListX86Lo = 1 << 61
559	RegListX86Hi = 1<<62 - 1
560)
561
562// RegisterRegisterList binds a pretty-printer (RLconv) for register list
563// numbers to a given register list number range. Lo is inclusive,
564// hi exclusive (valid register list are lo through hi-1).
565func RegisterRegisterList(lo, hi int64, rlconv func(int64) string) {
566	regListSpace = append(regListSpace, regListSet{lo, hi, rlconv})
567}
568
569func RLconv(list int64) string {
570	for i := range regListSpace {
571		rls := &regListSpace[i]
572		if rls.lo <= list && list < rls.hi {
573			return rls.RLconv(list)
574		}
575	}
576	return fmt.Sprintf("RL???%d", list)
577}
578
579// Special operands
580type spcSet struct {
581	lo      int64
582	hi      int64
583	SPCconv func(int64) string
584}
585
586var spcSpace []spcSet
587
588// RegisterSpecialOperands binds a pretty-printer (SPCconv) for special
589// operand numbers to a given special operand number range. Lo is inclusive,
590// hi is exclusive (valid special operands are lo through hi-1).
591func RegisterSpecialOperands(lo, hi int64, rlconv func(int64) string) {
592	spcSpace = append(spcSpace, spcSet{lo, hi, rlconv})
593}
594
595// SPCconv returns the string representation of the special operand spc.
596func SPCconv(spc int64) string {
597	for i := range spcSpace {
598		spcs := &spcSpace[i]
599		if spcs.lo <= spc && spc < spcs.hi {
600			return spcs.SPCconv(spc)
601		}
602	}
603	return fmt.Sprintf("SPC???%d", spc)
604}
605
606type opSet struct {
607	lo    As
608	names []string
609}
610
611// Not even worth sorting
612var aSpace []opSet
613
614// RegisterOpcode binds a list of instruction names
615// to a given instruction number range.
616func RegisterOpcode(lo As, Anames []string) {
617	if len(Anames) > AllowedOpCodes {
618		panic(fmt.Sprintf("too many instructions, have %d max %d", len(Anames), AllowedOpCodes))
619	}
620	aSpace = append(aSpace, opSet{lo, Anames})
621}
622
623func (a As) String() string {
624	if 0 <= a && int(a) < len(Anames) {
625		return Anames[a]
626	}
627	for i := range aSpace {
628		as := &aSpace[i]
629		if as.lo <= a && int(a-as.lo) < len(as.names) {
630			return as.names[a-as.lo]
631		}
632	}
633	return fmt.Sprintf("A???%d", a)
634}
635
636var Anames = []string{
637	"XXX",
638	"CALL",
639	"DUFFCOPY",
640	"DUFFZERO",
641	"END",
642	"FUNCDATA",
643	"JMP",
644	"NOP",
645	"PCALIGN",
646	"PCALIGNMAX",
647	"PCDATA",
648	"RET",
649	"GETCALLERPC",
650	"TEXT",
651	"UNDEF",
652}
653
654func Bool2int(b bool) int {
655	// The compiler currently only optimizes this form.
656	// See issue 6011.
657	var i int
658	if b {
659		i = 1
660	} else {
661		i = 0
662	}
663	return i
664}
665
666func abiDecorate(a *Addr, abiDetail bool) string {
667	if !abiDetail || a.Sym == nil {
668		return ""
669	}
670	return fmt.Sprintf("<%s>", a.Sym.ABI())
671}
672
673// AlignmentPadding bytes to add to align code as requested.
674// Alignment is restricted to powers of 2 between 8 and 2048 inclusive.
675//
676// pc_: current offset in function, in bytes
677// p:  a PCALIGN or PCALIGNMAX prog
678// ctxt: the context, for current function
679// cursym: current function being assembled
680// returns number of bytes of padding needed,
681// updates minimum alignment for the function.
682func AlignmentPadding(pc int32, p *Prog, ctxt *Link, cursym *LSym) int {
683	v := AlignmentPaddingLength(pc, p, ctxt)
684	requireAlignment(p.From.Offset, ctxt, cursym)
685	return v
686}
687
688// AlignmentPaddingLength is the number of bytes to add to align code as requested.
689// Alignment is restricted to powers of 2 between 8 and 2048 inclusive.
690// This only computes the length and does not update the (missing parameter)
691// current function's own required alignment.
692//
693// pc: current offset in function, in bytes
694// p:  a PCALIGN or PCALIGNMAX prog
695// ctxt: the context, for current function
696// returns number of bytes of padding needed,
697func AlignmentPaddingLength(pc int32, p *Prog, ctxt *Link) int {
698	a := p.From.Offset
699	if !((a&(a-1) == 0) && 8 <= a && a <= 2048) {
700		ctxt.Diag("alignment value of an instruction must be a power of two and in the range [8, 2048], got %d\n", a)
701		return 0
702	}
703	pc64 := int64(pc)
704	lob := pc64 & (a - 1) // Low Order Bits -- if not zero, then not aligned
705	if p.As == APCALIGN {
706		if lob != 0 {
707			return int(a - lob)
708		}
709		return 0
710	}
711	// emit as many as s bytes of padding to obtain alignment
712	s := p.To.Offset
713	if s < 0 || s >= a {
714		ctxt.Diag("PCALIGNMAX 'amount' %d must be non-negative and smaller than the aligment %d\n", s, a)
715		return 0
716	}
717	if s >= a-lob {
718		return int(a - lob)
719	}
720	return 0
721}
722
723// requireAlignment ensures that the function is aligned enough to support
724// the required code alignment
725func requireAlignment(a int64, ctxt *Link, cursym *LSym) {
726	// TODO remove explicit knowledge about AIX.
727	if ctxt.Headtype != objabi.Haix && cursym.Func().Align < int32(a) {
728		cursym.Func().Align = int32(a)
729	}
730}
731