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 := ®Space[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 := ®ListSpace[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