podman
1999 строк · 55.8 Кб
1// Copyright © 2015 The Go Authors. All rights reserved.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20
21package riscv
22
23import (
24"github.com/twitchyliquid64/golang-asm/obj"
25"github.com/twitchyliquid64/golang-asm/objabi"
26"github.com/twitchyliquid64/golang-asm/sys"
27"fmt"
28)
29
30func buildop(ctxt *obj.Link) {}
31
32// jalrToSym replaces p with a set of Progs needed to jump to the Sym in p.
33// lr is the link register to use for the JALR.
34// p must be a CALL, JMP or RET.
35func jalrToSym(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc, lr int16) *obj.Prog {
36if p.As != obj.ACALL && p.As != obj.AJMP && p.As != obj.ARET {
37ctxt.Diag("unexpected Prog in jalrToSym: %v", p)
38return p
39}
40
41// TODO(jsing): Consider using a single JAL instruction and teaching
42// the linker to provide trampolines for the case where the destination
43// offset is too large. This would potentially reduce instructions for
44// the common case, but would require three instructions to go via the
45// trampoline.
46
47to := p.To
48
49p.As = AAUIPC
50p.Mark |= NEED_PCREL_ITYPE_RELOC
51p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: to.Offset, Sym: to.Sym}}
52p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
53p.Reg = 0
54p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
55p = obj.Appendp(p, newprog)
56
57// Leave Sym only for the CALL reloc in assemble.
58p.As = AJALR
59p.From.Type = obj.TYPE_REG
60p.From.Reg = lr
61p.Reg = 0
62p.To.Type = obj.TYPE_REG
63p.To.Reg = REG_TMP
64p.To.Sym = to.Sym
65
66return p
67}
68
69// progedit is called individually for each *obj.Prog. It normalizes instruction
70// formats and eliminates as many pseudo-instructions as possible.
71func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
72
73// Expand binary instructions to ternary ones.
74if p.Reg == 0 {
75switch p.As {
76case AADDI, ASLTI, ASLTIU, AANDI, AORI, AXORI, ASLLI, ASRLI, ASRAI,
77AADD, AAND, AOR, AXOR, ASLL, ASRL, ASUB, ASRA,
78AMUL, AMULH, AMULHU, AMULHSU, AMULW, ADIV, ADIVU, ADIVW, ADIVUW,
79AREM, AREMU, AREMW, AREMUW:
80p.Reg = p.To.Reg
81}
82}
83
84// Rewrite instructions with constant operands to refer to the immediate
85// form of the instruction.
86if p.From.Type == obj.TYPE_CONST {
87switch p.As {
88case AADD:
89p.As = AADDI
90case ASLT:
91p.As = ASLTI
92case ASLTU:
93p.As = ASLTIU
94case AAND:
95p.As = AANDI
96case AOR:
97p.As = AORI
98case AXOR:
99p.As = AXORI
100case ASLL:
101p.As = ASLLI
102case ASRL:
103p.As = ASRLI
104case ASRA:
105p.As = ASRAI
106}
107}
108
109switch p.As {
110case obj.AJMP:
111// Turn JMP into JAL ZERO or JALR ZERO.
112p.From.Type = obj.TYPE_REG
113p.From.Reg = REG_ZERO
114
115switch p.To.Type {
116case obj.TYPE_BRANCH:
117p.As = AJAL
118case obj.TYPE_MEM:
119switch p.To.Name {
120case obj.NAME_NONE:
121p.As = AJALR
122case obj.NAME_EXTERN:
123// Handled in preprocess.
124default:
125ctxt.Diag("unsupported name %d for %v", p.To.Name, p)
126}
127default:
128panic(fmt.Sprintf("unhandled type %+v", p.To.Type))
129}
130
131case obj.ACALL:
132switch p.To.Type {
133case obj.TYPE_MEM:
134// Handled in preprocess.
135case obj.TYPE_REG:
136p.As = AJALR
137p.From.Type = obj.TYPE_REG
138p.From.Reg = REG_LR
139default:
140ctxt.Diag("unknown destination type %+v in CALL: %v", p.To.Type, p)
141}
142
143case obj.AUNDEF:
144p.As = AEBREAK
145
146case ASCALL:
147// SCALL is the old name for ECALL.
148p.As = AECALL
149
150case ASBREAK:
151// SBREAK is the old name for EBREAK.
152p.As = AEBREAK
153}
154}
155
156// addrToReg extracts the register from an Addr, handling special Addr.Names.
157func addrToReg(a obj.Addr) int16 {
158switch a.Name {
159case obj.NAME_PARAM, obj.NAME_AUTO:
160return REG_SP
161}
162return a.Reg
163}
164
165// movToLoad converts a MOV mnemonic into the corresponding load instruction.
166func movToLoad(mnemonic obj.As) obj.As {
167switch mnemonic {
168case AMOV:
169return ALD
170case AMOVB:
171return ALB
172case AMOVH:
173return ALH
174case AMOVW:
175return ALW
176case AMOVBU:
177return ALBU
178case AMOVHU:
179return ALHU
180case AMOVWU:
181return ALWU
182case AMOVF:
183return AFLW
184case AMOVD:
185return AFLD
186default:
187panic(fmt.Sprintf("%+v is not a MOV", mnemonic))
188}
189}
190
191// movToStore converts a MOV mnemonic into the corresponding store instruction.
192func movToStore(mnemonic obj.As) obj.As {
193switch mnemonic {
194case AMOV:
195return ASD
196case AMOVB:
197return ASB
198case AMOVH:
199return ASH
200case AMOVW:
201return ASW
202case AMOVF:
203return AFSW
204case AMOVD:
205return AFSD
206default:
207panic(fmt.Sprintf("%+v is not a MOV", mnemonic))
208}
209}
210
211// rewriteMOV rewrites MOV pseudo-instructions.
212func rewriteMOV(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog) {
213switch p.As {
214case AMOV, AMOVB, AMOVH, AMOVW, AMOVBU, AMOVHU, AMOVWU, AMOVF, AMOVD:
215default:
216panic(fmt.Sprintf("%+v is not a MOV pseudo-instruction", p.As))
217}
218
219switch p.From.Type {
220case obj.TYPE_MEM: // MOV c(Rs), Rd -> L $c, Rs, Rd
221switch p.From.Name {
222case obj.NAME_AUTO, obj.NAME_PARAM, obj.NAME_NONE:
223if p.To.Type != obj.TYPE_REG {
224ctxt.Diag("unsupported load at %v", p)
225}
226p.As = movToLoad(p.As)
227p.From.Reg = addrToReg(p.From)
228
229case obj.NAME_EXTERN, obj.NAME_STATIC:
230// AUIPC $off_hi, R
231// L $off_lo, R
232as := p.As
233to := p.To
234
235p.As = AAUIPC
236p.Mark |= NEED_PCREL_ITYPE_RELOC
237p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: p.From.Offset, Sym: p.From.Sym}}
238p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
239p.Reg = 0
240p.To = obj.Addr{Type: obj.TYPE_REG, Reg: to.Reg}
241p = obj.Appendp(p, newprog)
242
243p.As = movToLoad(as)
244p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: to.Reg, Offset: 0}
245p.To = to
246
247default:
248ctxt.Diag("unsupported name %d for %v", p.From.Name, p)
249}
250
251case obj.TYPE_REG:
252switch p.To.Type {
253case obj.TYPE_REG:
254switch p.As {
255case AMOV: // MOV Ra, Rb -> ADDI $0, Ra, Rb
256p.As = AADDI
257p.Reg = p.From.Reg
258p.From = obj.Addr{Type: obj.TYPE_CONST}
259
260case AMOVF: // MOVF Ra, Rb -> FSGNJS Ra, Ra, Rb
261p.As = AFSGNJS
262p.Reg = p.From.Reg
263
264case AMOVD: // MOVD Ra, Rb -> FSGNJD Ra, Ra, Rb
265p.As = AFSGNJD
266p.Reg = p.From.Reg
267
268default:
269ctxt.Diag("unsupported register-register move at %v", p)
270}
271
272case obj.TYPE_MEM: // MOV Rs, c(Rd) -> S $c, Rs, Rd
273switch p.As {
274case AMOVBU, AMOVHU, AMOVWU:
275ctxt.Diag("unsupported unsigned store at %v", p)
276}
277switch p.To.Name {
278case obj.NAME_AUTO, obj.NAME_PARAM, obj.NAME_NONE:
279p.As = movToStore(p.As)
280p.To.Reg = addrToReg(p.To)
281
282case obj.NAME_EXTERN:
283// AUIPC $off_hi, TMP
284// S $off_lo, TMP, R
285as := p.As
286from := p.From
287
288p.As = AAUIPC
289p.Mark |= NEED_PCREL_STYPE_RELOC
290p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: p.To.Offset, Sym: p.To.Sym}}
291p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
292p.Reg = 0
293p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
294p = obj.Appendp(p, newprog)
295
296p.As = movToStore(as)
297p.From = from
298p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_TMP, Offset: 0}
299
300default:
301ctxt.Diag("unsupported name %d for %v", p.From.Name, p)
302}
303
304default:
305ctxt.Diag("unsupported MOV at %v", p)
306}
307
308case obj.TYPE_CONST:
309// MOV $c, R
310// If c is small enough, convert to:
311// ADD $c, ZERO, R
312// If not, convert to:
313// LUI top20bits(c), R
314// ADD bottom12bits(c), R, R
315if p.As != AMOV {
316ctxt.Diag("unsupported constant load at %v", p)
317}
318off := p.From.Offset
319to := p.To
320
321low, high, err := Split32BitImmediate(off)
322if err != nil {
323ctxt.Diag("%v: constant %d too large: %v", p, off, err)
324}
325
326// LUI is only necessary if the offset doesn't fit in 12-bits.
327needLUI := high != 0
328if needLUI {
329p.As = ALUI
330p.To = to
331// Pass top 20 bits to LUI.
332p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: high}
333p = obj.Appendp(p, newprog)
334}
335p.As = AADDIW
336p.To = to
337p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: low}
338p.Reg = REG_ZERO
339if needLUI {
340p.Reg = to.Reg
341}
342
343case obj.TYPE_ADDR: // MOV $sym+off(SP/SB), R
344if p.To.Type != obj.TYPE_REG || p.As != AMOV {
345ctxt.Diag("unsupported addr MOV at %v", p)
346}
347switch p.From.Name {
348case obj.NAME_EXTERN, obj.NAME_STATIC:
349// AUIPC $off_hi, R
350// ADDI $off_lo, R
351to := p.To
352
353p.As = AAUIPC
354p.Mark |= NEED_PCREL_ITYPE_RELOC
355p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: p.From.Offset, Sym: p.From.Sym}}
356p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
357p.Reg = 0
358p.To = to
359p = obj.Appendp(p, newprog)
360
361p.As = AADDI
362p.From = obj.Addr{Type: obj.TYPE_CONST}
363p.Reg = to.Reg
364p.To = to
365
366case obj.NAME_PARAM, obj.NAME_AUTO:
367p.As = AADDI
368p.Reg = REG_SP
369p.From.Type = obj.TYPE_CONST
370
371case obj.NAME_NONE:
372p.As = AADDI
373p.Reg = p.From.Reg
374p.From.Type = obj.TYPE_CONST
375p.From.Reg = 0
376
377default:
378ctxt.Diag("bad addr MOV from name %v at %v", p.From.Name, p)
379}
380
381default:
382ctxt.Diag("unsupported MOV at %v", p)
383}
384}
385
386// InvertBranch inverts the condition of a conditional branch.
387func InvertBranch(as obj.As) obj.As {
388switch as {
389case ABEQ:
390return ABNE
391case ABEQZ:
392return ABNEZ
393case ABGE:
394return ABLT
395case ABGEU:
396return ABLTU
397case ABGEZ:
398return ABLTZ
399case ABGT:
400return ABLE
401case ABGTU:
402return ABLEU
403case ABGTZ:
404return ABLEZ
405case ABLE:
406return ABGT
407case ABLEU:
408return ABGTU
409case ABLEZ:
410return ABGTZ
411case ABLT:
412return ABGE
413case ABLTU:
414return ABGEU
415case ABLTZ:
416return ABGEZ
417case ABNE:
418return ABEQ
419case ABNEZ:
420return ABEQZ
421default:
422panic("InvertBranch: not a branch")
423}
424}
425
426// containsCall reports whether the symbol contains a CALL (or equivalent)
427// instruction. Must be called after progedit.
428func containsCall(sym *obj.LSym) bool {
429// CALLs are CALL or JAL(R) with link register LR.
430for p := sym.Func.Text; p != nil; p = p.Link {
431switch p.As {
432case obj.ACALL:
433return true
434case AJAL, AJALR:
435if p.From.Type == obj.TYPE_REG && p.From.Reg == REG_LR {
436return true
437}
438}
439}
440
441return false
442}
443
444// setPCs sets the Pc field in all instructions reachable from p.
445// It uses pc as the initial value.
446func setPCs(p *obj.Prog, pc int64) {
447for ; p != nil; p = p.Link {
448p.Pc = pc
449for _, ins := range instructionsForProg(p) {
450pc += int64(ins.length())
451}
452}
453}
454
455// stackOffset updates Addr offsets based on the current stack size.
456//
457// The stack looks like:
458// -------------------
459// | |
460// | PARAMs |
461// | |
462// | |
463// -------------------
464// | Parent RA | SP on function entry
465// -------------------
466// | |
467// | |
468// | AUTOs |
469// | |
470// | |
471// -------------------
472// | RA | SP during function execution
473// -------------------
474//
475// FixedFrameSize makes other packages aware of the space allocated for RA.
476//
477// A nicer version of this diagram can be found on slide 21 of the presentation
478// attached to:
479//
480// https://golang.org/issue/16922#issuecomment-243748180
481//
482func stackOffset(a *obj.Addr, stacksize int64) {
483switch a.Name {
484case obj.NAME_AUTO:
485// Adjust to the top of AUTOs.
486a.Offset += stacksize
487case obj.NAME_PARAM:
488// Adjust to the bottom of PARAMs.
489a.Offset += stacksize + 8
490}
491}
492
493// preprocess generates prologue and epilogue code, computes PC-relative branch
494// and jump offsets, and resolves pseudo-registers.
495//
496// preprocess is called once per linker symbol.
497//
498// When preprocess finishes, all instructions in the symbol are either
499// concrete, real RISC-V instructions or directive pseudo-ops like TEXT,
500// PCDATA, and FUNCDATA.
501func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
502if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
503return
504}
505
506// Generate the prologue.
507text := cursym.Func.Text
508if text.As != obj.ATEXT {
509ctxt.Diag("preprocess: found symbol that does not start with TEXT directive")
510return
511}
512
513stacksize := text.To.Offset
514if stacksize == -8 {
515// Historical way to mark NOFRAME.
516text.From.Sym.Set(obj.AttrNoFrame, true)
517stacksize = 0
518}
519if stacksize < 0 {
520ctxt.Diag("negative frame size %d - did you mean NOFRAME?", stacksize)
521}
522if text.From.Sym.NoFrame() {
523if stacksize != 0 {
524ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", stacksize)
525}
526}
527
528if !containsCall(cursym) {
529text.From.Sym.Set(obj.AttrLeaf, true)
530if stacksize == 0 {
531// A leaf function with no locals has no frame.
532text.From.Sym.Set(obj.AttrNoFrame, true)
533}
534}
535
536// Save LR unless there is no frame.
537if !text.From.Sym.NoFrame() {
538stacksize += ctxt.FixedFrameSize()
539}
540
541cursym.Func.Args = text.To.Val.(int32)
542cursym.Func.Locals = int32(stacksize)
543
544prologue := text
545
546if !cursym.Func.Text.From.Sym.NoSplit() {
547prologue = stacksplit(ctxt, prologue, cursym, newprog, stacksize) // emit split check
548}
549
550if stacksize != 0 {
551prologue = ctxt.StartUnsafePoint(prologue, newprog)
552
553// Actually save LR.
554prologue = obj.Appendp(prologue, newprog)
555prologue.As = AMOV
556prologue.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
557prologue.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: -stacksize}
558
559// Insert stack adjustment.
560prologue = obj.Appendp(prologue, newprog)
561prologue.As = AADDI
562prologue.From = obj.Addr{Type: obj.TYPE_CONST, Offset: -stacksize}
563prologue.Reg = REG_SP
564prologue.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_SP}
565prologue.Spadj = int32(stacksize)
566
567prologue = ctxt.EndUnsafePoint(prologue, newprog, -1)
568}
569
570if cursym.Func.Text.From.Sym.Wrapper() {
571// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
572//
573// MOV g_panic(g), X11
574// BNE X11, ZERO, adjust
575// end:
576// NOP
577// ...rest of function..
578// adjust:
579// MOV panic_argp(X11), X12
580// ADD $(autosize+FIXED_FRAME), SP, X13
581// BNE X12, X13, end
582// ADD $FIXED_FRAME, SP, X12
583// MOV X12, panic_argp(X11)
584// JMP end
585//
586// The NOP is needed to give the jumps somewhere to land.
587
588ldpanic := obj.Appendp(prologue, newprog)
589
590ldpanic.As = AMOV
591ldpanic.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REGG, Offset: 4 * int64(ctxt.Arch.PtrSize)} // G.panic
592ldpanic.Reg = 0
593ldpanic.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X11}
594
595bneadj := obj.Appendp(ldpanic, newprog)
596bneadj.As = ABNE
597bneadj.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X11}
598bneadj.Reg = REG_ZERO
599bneadj.To.Type = obj.TYPE_BRANCH
600
601endadj := obj.Appendp(bneadj, newprog)
602endadj.As = obj.ANOP
603
604last := endadj
605for last.Link != nil {
606last = last.Link
607}
608
609getargp := obj.Appendp(last, newprog)
610getargp.As = AMOV
611getargp.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_X11, Offset: 0} // Panic.argp
612getargp.Reg = 0
613getargp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X12}
614
615bneadj.To.SetTarget(getargp)
616
617calcargp := obj.Appendp(getargp, newprog)
618calcargp.As = AADDI
619calcargp.From = obj.Addr{Type: obj.TYPE_CONST, Offset: stacksize + ctxt.FixedFrameSize()}
620calcargp.Reg = REG_SP
621calcargp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X13}
622
623testargp := obj.Appendp(calcargp, newprog)
624testargp.As = ABNE
625testargp.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X12}
626testargp.Reg = REG_X13
627testargp.To.Type = obj.TYPE_BRANCH
628testargp.To.SetTarget(endadj)
629
630adjargp := obj.Appendp(testargp, newprog)
631adjargp.As = AADDI
632adjargp.From = obj.Addr{Type: obj.TYPE_CONST, Offset: int64(ctxt.Arch.PtrSize)}
633adjargp.Reg = REG_SP
634adjargp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X12}
635
636setargp := obj.Appendp(adjargp, newprog)
637setargp.As = AMOV
638setargp.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X12}
639setargp.Reg = 0
640setargp.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_X11, Offset: 0} // Panic.argp
641
642godone := obj.Appendp(setargp, newprog)
643godone.As = AJAL
644godone.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO}
645godone.To.Type = obj.TYPE_BRANCH
646godone.To.SetTarget(endadj)
647}
648
649// Update stack-based offsets.
650for p := cursym.Func.Text; p != nil; p = p.Link {
651stackOffset(&p.From, stacksize)
652stackOffset(&p.To, stacksize)
653}
654
655// Additional instruction rewriting.
656for p := cursym.Func.Text; p != nil; p = p.Link {
657switch p.As {
658case obj.AGETCALLERPC:
659if cursym.Leaf() {
660// MOV LR, Rd
661p.As = AMOV
662p.From.Type = obj.TYPE_REG
663p.From.Reg = REG_LR
664} else {
665// MOV (RSP), Rd
666p.As = AMOV
667p.From.Type = obj.TYPE_MEM
668p.From.Reg = REG_SP
669}
670
671case obj.ACALL:
672switch p.To.Type {
673case obj.TYPE_MEM:
674jalrToSym(ctxt, p, newprog, REG_LR)
675}
676
677case obj.AJMP:
678switch p.To.Type {
679case obj.TYPE_MEM:
680switch p.To.Name {
681case obj.NAME_EXTERN:
682// JMP to symbol.
683jalrToSym(ctxt, p, newprog, REG_ZERO)
684}
685}
686
687case obj.ARET:
688// Replace RET with epilogue.
689retJMP := p.To.Sym
690
691if stacksize != 0 {
692// Restore LR.
693p.As = AMOV
694p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: 0}
695p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
696p = obj.Appendp(p, newprog)
697
698p.As = AADDI
699p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: stacksize}
700p.Reg = REG_SP
701p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_SP}
702p.Spadj = int32(-stacksize)
703p = obj.Appendp(p, newprog)
704}
705
706if retJMP != nil {
707p.As = obj.ARET
708p.To.Sym = retJMP
709p = jalrToSym(ctxt, p, newprog, REG_ZERO)
710} else {
711p.As = AJALR
712p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO}
713p.Reg = 0
714p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
715}
716
717// "Add back" the stack removed in the previous instruction.
718//
719// This is to avoid confusing pctospadj, which sums
720// Spadj from function entry to each PC, and shouldn't
721// count adjustments from earlier epilogues, since they
722// won't affect later PCs.
723p.Spadj = int32(stacksize)
724
725case AADDI:
726// Refine Spadjs account for adjustment via ADDI instruction.
727if p.To.Type == obj.TYPE_REG && p.To.Reg == REG_SP && p.From.Type == obj.TYPE_CONST {
728p.Spadj = int32(-p.From.Offset)
729}
730}
731}
732
733// Rewrite MOV pseudo-instructions. This cannot be done in
734// progedit, as SP offsets need to be applied before we split
735// up some of the Addrs.
736for p := cursym.Func.Text; p != nil; p = p.Link {
737switch p.As {
738case AMOV, AMOVB, AMOVH, AMOVW, AMOVBU, AMOVHU, AMOVWU, AMOVF, AMOVD:
739rewriteMOV(ctxt, newprog, p)
740}
741}
742
743// Split immediates larger than 12-bits.
744for p := cursym.Func.Text; p != nil; p = p.Link {
745switch p.As {
746// <opi> $imm, REG, TO
747case AADDI, AANDI, AORI, AXORI:
748// LUI $high, TMP
749// ADDI $low, TMP, TMP
750// <op> TMP, REG, TO
751q := *p
752low, high, err := Split32BitImmediate(p.From.Offset)
753if err != nil {
754ctxt.Diag("%v: constant %d too large", p, p.From.Offset, err)
755}
756if high == 0 {
757break // no need to split
758}
759
760p.As = ALUI
761p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: high}
762p.Reg = 0
763p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
764p.Spadj = 0 // needed if TO is SP
765p = obj.Appendp(p, newprog)
766
767p.As = AADDIW
768p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: low}
769p.Reg = REG_TMP
770p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
771p = obj.Appendp(p, newprog)
772
773switch q.As {
774case AADDI:
775p.As = AADD
776case AANDI:
777p.As = AAND
778case AORI:
779p.As = AOR
780case AXORI:
781p.As = AXOR
782default:
783ctxt.Diag("unsupported instruction %v for splitting", q)
784}
785p.Spadj = q.Spadj
786p.To = q.To
787p.Reg = q.Reg
788p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
789
790// <load> $imm, REG, TO (load $imm+(REG), TO)
791case ALD, ALB, ALH, ALW, ALBU, ALHU, ALWU, AFLW, AFLD:
792low, high, err := Split32BitImmediate(p.From.Offset)
793if err != nil {
794ctxt.Diag("%v: constant %d too large", p, p.From.Offset)
795}
796if high == 0 {
797break // no need to split
798}
799q := *p
800
801// LUI $high, TMP
802// ADD TMP, REG, TMP
803// <load> $low, TMP, TO
804p.As = ALUI
805p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: high}
806p.Reg = 0
807p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
808p.Spadj = 0 // needed if TO is SP
809p = obj.Appendp(p, newprog)
810
811p.As = AADD
812p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
813p.Reg = q.From.Reg
814p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
815p = obj.Appendp(p, newprog)
816
817p.As = q.As
818p.To = q.To
819p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_TMP, Offset: low}
820p.Reg = obj.REG_NONE
821
822// <store> $imm, REG, TO (store $imm+(TO), REG)
823case ASD, ASB, ASH, ASW, AFSW, AFSD:
824low, high, err := Split32BitImmediate(p.To.Offset)
825if err != nil {
826ctxt.Diag("%v: constant %d too large", p, p.To.Offset)
827}
828if high == 0 {
829break // no need to split
830}
831q := *p
832
833// LUI $high, TMP
834// ADD TMP, TO, TMP
835// <store> $low, REG, TMP
836p.As = ALUI
837p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: high}
838p.Reg = 0
839p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
840p.Spadj = 0 // needed if TO is SP
841p = obj.Appendp(p, newprog)
842
843p.As = AADD
844p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
845p.Reg = q.To.Reg
846p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
847p = obj.Appendp(p, newprog)
848
849p.As = q.As
850p.From = obj.Addr{Type: obj.TYPE_REG, Reg: q.From.Reg, Offset: 0}
851p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_TMP, Offset: low}
852}
853}
854
855// Compute instruction addresses. Once we do that, we need to check for
856// overextended jumps and branches. Within each iteration, Pc differences
857// are always lower bounds (since the program gets monotonically longer,
858// a fixed point will be reached). No attempt to handle functions > 2GiB.
859for {
860rescan := false
861setPCs(cursym.Func.Text, 0)
862
863for p := cursym.Func.Text; p != nil; p = p.Link {
864switch p.As {
865case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ:
866if p.To.Type != obj.TYPE_BRANCH {
867panic("assemble: instruction with branch-like opcode lacks destination")
868}
869offset := p.To.Target().Pc - p.Pc
870if offset < -4096 || 4096 <= offset {
871// Branch is long. Replace it with a jump.
872jmp := obj.Appendp(p, newprog)
873jmp.As = AJAL
874jmp.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO}
875jmp.To = obj.Addr{Type: obj.TYPE_BRANCH}
876jmp.To.SetTarget(p.To.Target())
877
878p.As = InvertBranch(p.As)
879p.To.SetTarget(jmp.Link)
880
881// We may have made previous branches too long,
882// so recheck them.
883rescan = true
884}
885case AJAL:
886if p.To.Target() == nil {
887panic("intersymbol jumps should be expressed as AUIPC+JALR")
888}
889offset := p.To.Target().Pc - p.Pc
890if offset < -(1<<20) || (1<<20) <= offset {
891// Replace with 2-instruction sequence. This assumes
892// that TMP is not live across J instructions, since
893// it is reserved by SSA.
894jmp := obj.Appendp(p, newprog)
895jmp.As = AJALR
896jmp.From = p.From
897jmp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
898
899// p.From is not generally valid, however will be
900// fixed up in the next loop.
901p.As = AAUIPC
902p.From = obj.Addr{Type: obj.TYPE_BRANCH, Sym: p.From.Sym}
903p.From.SetTarget(p.To.Target())
904p.Reg = 0
905p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
906
907rescan = true
908}
909}
910}
911
912if !rescan {
913break
914}
915}
916
917// Now that there are no long branches, resolve branch and jump targets.
918// At this point, instruction rewriting which changes the number of
919// instructions will break everything--don't do it!
920for p := cursym.Func.Text; p != nil; p = p.Link {
921switch p.As {
922case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ, AJAL:
923switch p.To.Type {
924case obj.TYPE_BRANCH:
925p.To.Type, p.To.Offset = obj.TYPE_CONST, p.To.Target().Pc-p.Pc
926case obj.TYPE_MEM:
927panic("unhandled type")
928}
929
930case AAUIPC:
931if p.From.Type == obj.TYPE_BRANCH {
932low, high, err := Split32BitImmediate(p.From.Target().Pc - p.Pc)
933if err != nil {
934ctxt.Diag("%v: jump displacement %d too large", p, p.To.Target().Pc-p.Pc)
935}
936p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: high, Sym: cursym}
937p.Link.From.Offset = low
938}
939}
940}
941
942// Validate all instructions - this provides nice error messages.
943for p := cursym.Func.Text; p != nil; p = p.Link {
944for _, ins := range instructionsForProg(p) {
945ins.validate(ctxt)
946}
947}
948}
949
950func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgAlloc, framesize int64) *obj.Prog {
951// Leaf function with no frame is effectively NOSPLIT.
952if framesize == 0 {
953return p
954}
955
956// MOV g_stackguard(g), X10
957p = obj.Appendp(p, newprog)
958p.As = AMOV
959p.From.Type = obj.TYPE_MEM
960p.From.Reg = REGG
961p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
962if cursym.CFunc() {
963p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
964}
965p.To.Type = obj.TYPE_REG
966p.To.Reg = REG_X10
967
968var to_done, to_more *obj.Prog
969
970if framesize <= objabi.StackSmall {
971// small stack: SP < stackguard
972// BLTU SP, stackguard, done
973p = obj.Appendp(p, newprog)
974p.As = ABLTU
975p.From.Type = obj.TYPE_REG
976p.From.Reg = REG_X10
977p.Reg = REG_SP
978p.To.Type = obj.TYPE_BRANCH
979to_done = p
980} else if framesize <= objabi.StackBig {
981// large stack: SP-framesize < stackguard-StackSmall
982// ADD $-(framesize-StackSmall), SP, X11
983// BLTU X11, stackguard, done
984p = obj.Appendp(p, newprog)
985// TODO(sorear): logic inconsistent with comment, but both match all non-x86 arches
986p.As = AADDI
987p.From.Type = obj.TYPE_CONST
988p.From.Offset = -(int64(framesize) - objabi.StackSmall)
989p.Reg = REG_SP
990p.To.Type = obj.TYPE_REG
991p.To.Reg = REG_X11
992
993p = obj.Appendp(p, newprog)
994p.As = ABLTU
995p.From.Type = obj.TYPE_REG
996p.From.Reg = REG_X10
997p.Reg = REG_X11
998p.To.Type = obj.TYPE_BRANCH
999to_done = p
1000} else {
1001// Such a large stack we need to protect against wraparound.
1002// If SP is close to zero:
1003// SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
1004// The +StackGuard on both sides is required to keep the left side positive:
1005// SP is allowed to be slightly below stackguard. See stack.h.
1006//
1007// Preemption sets stackguard to StackPreempt, a very large value.
1008// That breaks the math above, so we have to check for that explicitly.
1009// // stackguard is X10
1010// MOV $StackPreempt, X11
1011// BEQ X10, X11, more
1012// ADD $StackGuard, SP, X11
1013// SUB X10, X11
1014// MOV $(framesize+(StackGuard-StackSmall)), X10
1015// BGTU X11, X10, done
1016p = obj.Appendp(p, newprog)
1017p.As = AMOV
1018p.From.Type = obj.TYPE_CONST
1019p.From.Offset = objabi.StackPreempt
1020p.To.Type = obj.TYPE_REG
1021p.To.Reg = REG_X11
1022
1023p = obj.Appendp(p, newprog)
1024to_more = p
1025p.As = ABEQ
1026p.From.Type = obj.TYPE_REG
1027p.From.Reg = REG_X10
1028p.Reg = REG_X11
1029p.To.Type = obj.TYPE_BRANCH
1030
1031p = obj.Appendp(p, newprog)
1032p.As = AADDI
1033p.From.Type = obj.TYPE_CONST
1034p.From.Offset = int64(objabi.StackGuard)
1035p.Reg = REG_SP
1036p.To.Type = obj.TYPE_REG
1037p.To.Reg = REG_X11
1038
1039p = obj.Appendp(p, newprog)
1040p.As = ASUB
1041p.From.Type = obj.TYPE_REG
1042p.From.Reg = REG_X10
1043p.Reg = REG_X11
1044p.To.Type = obj.TYPE_REG
1045p.To.Reg = REG_X11
1046
1047p = obj.Appendp(p, newprog)
1048p.As = AMOV
1049p.From.Type = obj.TYPE_CONST
1050p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall
1051p.To.Type = obj.TYPE_REG
1052p.To.Reg = REG_X10
1053
1054p = obj.Appendp(p, newprog)
1055p.As = ABLTU
1056p.From.Type = obj.TYPE_REG
1057p.From.Reg = REG_X10
1058p.Reg = REG_X11
1059p.To.Type = obj.TYPE_BRANCH
1060to_done = p
1061}
1062
1063p = ctxt.EmitEntryLiveness(cursym, p, newprog)
1064
1065// CALL runtime.morestack(SB)
1066p = obj.Appendp(p, newprog)
1067p.As = obj.ACALL
1068p.To.Type = obj.TYPE_BRANCH
1069if cursym.CFunc() {
1070p.To.Sym = ctxt.Lookup("runtime.morestackc")
1071} else if !cursym.Func.Text.From.Sym.NeedCtxt() {
1072p.To.Sym = ctxt.Lookup("runtime.morestack_noctxt")
1073} else {
1074p.To.Sym = ctxt.Lookup("runtime.morestack")
1075}
1076if to_more != nil {
1077to_more.To.SetTarget(p)
1078}
1079p = jalrToSym(ctxt, p, newprog, REG_X5)
1080
1081// JMP start
1082p = obj.Appendp(p, newprog)
1083p.As = AJAL
1084p.To = obj.Addr{Type: obj.TYPE_BRANCH}
1085p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO}
1086p.To.SetTarget(cursym.Func.Text.Link)
1087
1088// placeholder for to_done's jump target
1089p = obj.Appendp(p, newprog)
1090p.As = obj.ANOP // zero-width place holder
1091to_done.To.SetTarget(p)
1092
1093return p
1094}
1095
1096// signExtend sign extends val starting at bit bit.
1097func signExtend(val int64, bit uint) int64 {
1098return val << (64 - bit) >> (64 - bit)
1099}
1100
1101// Split32BitImmediate splits a signed 32-bit immediate into a signed 20-bit
1102// upper immediate and a signed 12-bit lower immediate to be added to the upper
1103// result. For example, high may be used in LUI and low in a following ADDI to
1104// generate a full 32-bit constant.
1105func Split32BitImmediate(imm int64) (low, high int64, err error) {
1106if !immIFits(imm, 32) {
1107return 0, 0, fmt.Errorf("immediate does not fit in 32-bits: %d", imm)
1108}
1109
1110// Nothing special needs to be done if the immediate fits in 12-bits.
1111if immIFits(imm, 12) {
1112return imm, 0, nil
1113}
1114
1115high = imm >> 12
1116
1117// The bottom 12 bits will be treated as signed.
1118//
1119// If that will result in a negative 12 bit number, add 1 to
1120// our upper bits to adjust for the borrow.
1121//
1122// It is not possible for this increment to overflow. To
1123// overflow, the 20 top bits would be 1, and the sign bit for
1124// the low 12 bits would be set, in which case the entire 32
1125// bit pattern fits in a 12 bit signed value.
1126if imm&(1<<11) != 0 {
1127high++
1128}
1129
1130low = signExtend(imm, 12)
1131high = signExtend(high, 20)
1132
1133return low, high, nil
1134}
1135
1136func regVal(r, min, max uint32) uint32 {
1137if r < min || r > max {
1138panic(fmt.Sprintf("register out of range, want %d < %d < %d", min, r, max))
1139}
1140return r - min
1141}
1142
1143// regI returns an integer register.
1144func regI(r uint32) uint32 {
1145return regVal(r, REG_X0, REG_X31)
1146}
1147
1148// regF returns a float register.
1149func regF(r uint32) uint32 {
1150return regVal(r, REG_F0, REG_F31)
1151}
1152
1153// regAddr extracts a register from an Addr.
1154func regAddr(a obj.Addr, min, max uint32) uint32 {
1155if a.Type != obj.TYPE_REG {
1156panic(fmt.Sprintf("ill typed: %+v", a))
1157}
1158return regVal(uint32(a.Reg), min, max)
1159}
1160
1161// regIAddr extracts the integer register from an Addr.
1162func regIAddr(a obj.Addr) uint32 {
1163return regAddr(a, REG_X0, REG_X31)
1164}
1165
1166// regFAddr extracts the float register from an Addr.
1167func regFAddr(a obj.Addr) uint32 {
1168return regAddr(a, REG_F0, REG_F31)
1169}
1170
1171// immIFits reports whether immediate value x fits in nbits bits
1172// as a signed integer.
1173func immIFits(x int64, nbits uint) bool {
1174nbits--
1175var min int64 = -1 << nbits
1176var max int64 = 1<<nbits - 1
1177return min <= x && x <= max
1178}
1179
1180// immI extracts the signed integer of the specified size from an immediate.
1181func immI(as obj.As, imm int64, nbits uint) uint32 {
1182if !immIFits(imm, nbits) {
1183panic(fmt.Sprintf("%v\tsigned immediate %d cannot fit in %d bits", as, imm, nbits))
1184}
1185return uint32(imm)
1186}
1187
1188func wantImmI(ctxt *obj.Link, as obj.As, imm int64, nbits uint) {
1189if !immIFits(imm, nbits) {
1190ctxt.Diag("%v\tsigned immediate cannot be larger than %d bits but got %d", as, nbits, imm)
1191}
1192}
1193
1194func wantReg(ctxt *obj.Link, as obj.As, pos string, descr string, r, min, max uint32) {
1195if r < min || r > max {
1196var suffix string
1197if r != obj.REG_NONE {
1198suffix = fmt.Sprintf(" but got non-%s register %s", descr, RegName(int(r)))
1199}
1200ctxt.Diag("%v\texpected %s register in %s position%s", as, descr, pos, suffix)
1201}
1202}
1203
1204func wantNoneReg(ctxt *obj.Link, as obj.As, pos string, r uint32) {
1205if r != obj.REG_NONE {
1206ctxt.Diag("%v\texpected no register in %s but got register %s", as, pos, RegName(int(r)))
1207}
1208}
1209
1210// wantIntReg checks that r is an integer register.
1211func wantIntReg(ctxt *obj.Link, as obj.As, pos string, r uint32) {
1212wantReg(ctxt, as, pos, "integer", r, REG_X0, REG_X31)
1213}
1214
1215// wantFloatReg checks that r is a floating-point register.
1216func wantFloatReg(ctxt *obj.Link, as obj.As, pos string, r uint32) {
1217wantReg(ctxt, as, pos, "float", r, REG_F0, REG_F31)
1218}
1219
1220// wantEvenOffset checks that the offset is a multiple of two.
1221func wantEvenOffset(ctxt *obj.Link, as obj.As, offset int64) {
1222if offset%1 != 0 {
1223ctxt.Diag("%v\tjump offset %v must be even", as, offset)
1224}
1225}
1226
1227func validateRIII(ctxt *obj.Link, ins *instruction) {
1228wantIntReg(ctxt, ins.as, "rd", ins.rd)
1229wantIntReg(ctxt, ins.as, "rs1", ins.rs1)
1230wantIntReg(ctxt, ins.as, "rs2", ins.rs2)
1231}
1232
1233func validateRFFF(ctxt *obj.Link, ins *instruction) {
1234wantFloatReg(ctxt, ins.as, "rd", ins.rd)
1235wantFloatReg(ctxt, ins.as, "rs1", ins.rs1)
1236wantFloatReg(ctxt, ins.as, "rs2", ins.rs2)
1237}
1238
1239func validateRFFI(ctxt *obj.Link, ins *instruction) {
1240wantIntReg(ctxt, ins.as, "rd", ins.rd)
1241wantFloatReg(ctxt, ins.as, "rs1", ins.rs1)
1242wantFloatReg(ctxt, ins.as, "rs2", ins.rs2)
1243}
1244
1245func validateRFI(ctxt *obj.Link, ins *instruction) {
1246wantIntReg(ctxt, ins.as, "rd", ins.rd)
1247wantNoneReg(ctxt, ins.as, "rs1", ins.rs1)
1248wantFloatReg(ctxt, ins.as, "rs2", ins.rs2)
1249}
1250
1251func validateRIF(ctxt *obj.Link, ins *instruction) {
1252wantFloatReg(ctxt, ins.as, "rd", ins.rd)
1253wantNoneReg(ctxt, ins.as, "rs1", ins.rs1)
1254wantIntReg(ctxt, ins.as, "rs2", ins.rs2)
1255}
1256
1257func validateRFF(ctxt *obj.Link, ins *instruction) {
1258wantFloatReg(ctxt, ins.as, "rd", ins.rd)
1259wantNoneReg(ctxt, ins.as, "rs1", ins.rs1)
1260wantFloatReg(ctxt, ins.as, "rs2", ins.rs2)
1261}
1262
1263func validateII(ctxt *obj.Link, ins *instruction) {
1264wantImmI(ctxt, ins.as, ins.imm, 12)
1265wantIntReg(ctxt, ins.as, "rd", ins.rd)
1266wantIntReg(ctxt, ins.as, "rs1", ins.rs1)
1267}
1268
1269func validateIF(ctxt *obj.Link, ins *instruction) {
1270wantImmI(ctxt, ins.as, ins.imm, 12)
1271wantFloatReg(ctxt, ins.as, "rd", ins.rd)
1272wantIntReg(ctxt, ins.as, "rs1", ins.rs1)
1273}
1274
1275func validateSI(ctxt *obj.Link, ins *instruction) {
1276wantImmI(ctxt, ins.as, ins.imm, 12)
1277wantIntReg(ctxt, ins.as, "rd", ins.rd)
1278wantIntReg(ctxt, ins.as, "rs1", ins.rs1)
1279}
1280
1281func validateSF(ctxt *obj.Link, ins *instruction) {
1282wantImmI(ctxt, ins.as, ins.imm, 12)
1283wantIntReg(ctxt, ins.as, "rd", ins.rd)
1284wantFloatReg(ctxt, ins.as, "rs1", ins.rs1)
1285}
1286
1287func validateB(ctxt *obj.Link, ins *instruction) {
1288// Offsets are multiples of two, so accept 13 bit immediates for the
1289// 12 bit slot. We implicitly drop the least significant bit in encodeB.
1290wantEvenOffset(ctxt, ins.as, ins.imm)
1291wantImmI(ctxt, ins.as, ins.imm, 13)
1292wantNoneReg(ctxt, ins.as, "rd", ins.rd)
1293wantIntReg(ctxt, ins.as, "rs1", ins.rs1)
1294wantIntReg(ctxt, ins.as, "rs2", ins.rs2)
1295}
1296
1297func validateU(ctxt *obj.Link, ins *instruction) {
1298wantImmI(ctxt, ins.as, ins.imm, 20)
1299wantIntReg(ctxt, ins.as, "rd", ins.rd)
1300wantNoneReg(ctxt, ins.as, "rs1", ins.rs1)
1301wantNoneReg(ctxt, ins.as, "rs2", ins.rs2)
1302}
1303
1304func validateJ(ctxt *obj.Link, ins *instruction) {
1305// Offsets are multiples of two, so accept 21 bit immediates for the
1306// 20 bit slot. We implicitly drop the least significant bit in encodeJ.
1307wantEvenOffset(ctxt, ins.as, ins.imm)
1308wantImmI(ctxt, ins.as, ins.imm, 21)
1309wantIntReg(ctxt, ins.as, "rd", ins.rd)
1310wantNoneReg(ctxt, ins.as, "rs1", ins.rs1)
1311wantNoneReg(ctxt, ins.as, "rs2", ins.rs2)
1312}
1313
1314func validateRaw(ctxt *obj.Link, ins *instruction) {
1315// Treat the raw value specially as a 32-bit unsigned integer.
1316// Nobody wants to enter negative machine code.
1317if ins.imm < 0 || 1<<32 <= ins.imm {
1318ctxt.Diag("%v\timmediate in raw position cannot be larger than 32 bits but got %d", ins.as, ins.imm)
1319}
1320}
1321
1322// encodeR encodes an R-type RISC-V instruction.
1323func encodeR(as obj.As, rs1, rs2, rd, funct3, funct7 uint32) uint32 {
1324enc := encode(as)
1325if enc == nil {
1326panic("encodeR: could not encode instruction")
1327}
1328if enc.rs2 != 0 && rs2 != 0 {
1329panic("encodeR: instruction uses rs2, but rs2 was nonzero")
1330}
1331return funct7<<25 | enc.funct7<<25 | enc.rs2<<20 | rs2<<20 | rs1<<15 | enc.funct3<<12 | funct3<<12 | rd<<7 | enc.opcode
1332}
1333
1334func encodeRIII(ins *instruction) uint32 {
1335return encodeR(ins.as, regI(ins.rs1), regI(ins.rs2), regI(ins.rd), ins.funct3, ins.funct7)
1336}
1337
1338func encodeRFFF(ins *instruction) uint32 {
1339return encodeR(ins.as, regF(ins.rs1), regF(ins.rs2), regF(ins.rd), ins.funct3, ins.funct7)
1340}
1341
1342func encodeRFFI(ins *instruction) uint32 {
1343return encodeR(ins.as, regF(ins.rs1), regF(ins.rs2), regI(ins.rd), ins.funct3, ins.funct7)
1344}
1345
1346func encodeRFI(ins *instruction) uint32 {
1347return encodeR(ins.as, regF(ins.rs2), 0, regI(ins.rd), ins.funct3, ins.funct7)
1348}
1349
1350func encodeRIF(ins *instruction) uint32 {
1351return encodeR(ins.as, regI(ins.rs2), 0, regF(ins.rd), ins.funct3, ins.funct7)
1352}
1353
1354func encodeRFF(ins *instruction) uint32 {
1355return encodeR(ins.as, regF(ins.rs2), 0, regF(ins.rd), ins.funct3, ins.funct7)
1356}
1357
1358// encodeI encodes an I-type RISC-V instruction.
1359func encodeI(as obj.As, rs1, rd, imm uint32) uint32 {
1360enc := encode(as)
1361if enc == nil {
1362panic("encodeI: could not encode instruction")
1363}
1364imm |= uint32(enc.csr)
1365return imm<<20 | rs1<<15 | enc.funct3<<12 | rd<<7 | enc.opcode
1366}
1367
1368func encodeII(ins *instruction) uint32 {
1369return encodeI(ins.as, regI(ins.rs1), regI(ins.rd), uint32(ins.imm))
1370}
1371
1372func encodeIF(ins *instruction) uint32 {
1373return encodeI(ins.as, regI(ins.rs1), regF(ins.rd), uint32(ins.imm))
1374}
1375
1376// encodeS encodes an S-type RISC-V instruction.
1377func encodeS(as obj.As, rs1, rs2, imm uint32) uint32 {
1378enc := encode(as)
1379if enc == nil {
1380panic("encodeS: could not encode instruction")
1381}
1382return (imm>>5)<<25 | rs2<<20 | rs1<<15 | enc.funct3<<12 | (imm&0x1f)<<7 | enc.opcode
1383}
1384
1385func encodeSI(ins *instruction) uint32 {
1386return encodeS(ins.as, regI(ins.rd), regI(ins.rs1), uint32(ins.imm))
1387}
1388
1389func encodeSF(ins *instruction) uint32 {
1390return encodeS(ins.as, regI(ins.rd), regF(ins.rs1), uint32(ins.imm))
1391}
1392
1393// encodeB encodes a B-type RISC-V instruction.
1394func encodeB(ins *instruction) uint32 {
1395imm := immI(ins.as, ins.imm, 13)
1396rs2 := regI(ins.rs1)
1397rs1 := regI(ins.rs2)
1398enc := encode(ins.as)
1399if enc == nil {
1400panic("encodeB: could not encode instruction")
1401}
1402return (imm>>12)<<31 | ((imm>>5)&0x3f)<<25 | rs2<<20 | rs1<<15 | enc.funct3<<12 | ((imm>>1)&0xf)<<8 | ((imm>>11)&0x1)<<7 | enc.opcode
1403}
1404
1405// encodeU encodes a U-type RISC-V instruction.
1406func encodeU(ins *instruction) uint32 {
1407// The immediates for encodeU are the upper 20 bits of a 32 bit value.
1408// Rather than have the user/compiler generate a 32 bit constant, the
1409// bottommost bits of which must all be zero, instead accept just the
1410// top bits.
1411imm := immI(ins.as, ins.imm, 20)
1412rd := regI(ins.rd)
1413enc := encode(ins.as)
1414if enc == nil {
1415panic("encodeU: could not encode instruction")
1416}
1417return imm<<12 | rd<<7 | enc.opcode
1418}
1419
1420// encodeJ encodes a J-type RISC-V instruction.
1421func encodeJ(ins *instruction) uint32 {
1422imm := immI(ins.as, ins.imm, 21)
1423rd := regI(ins.rd)
1424enc := encode(ins.as)
1425if enc == nil {
1426panic("encodeJ: could not encode instruction")
1427}
1428return (imm>>20)<<31 | ((imm>>1)&0x3ff)<<21 | ((imm>>11)&0x1)<<20 | ((imm>>12)&0xff)<<12 | rd<<7 | enc.opcode
1429}
1430
1431func encodeRawIns(ins *instruction) uint32 {
1432// Treat the raw value specially as a 32-bit unsigned integer.
1433// Nobody wants to enter negative machine code.
1434if ins.imm < 0 || 1<<32 <= ins.imm {
1435panic(fmt.Sprintf("immediate %d cannot fit in 32 bits", ins.imm))
1436}
1437return uint32(ins.imm)
1438}
1439
1440func EncodeIImmediate(imm int64) (int64, error) {
1441if !immIFits(imm, 12) {
1442return 0, fmt.Errorf("immediate %#x does not fit in 12 bits", imm)
1443}
1444return imm << 20, nil
1445}
1446
1447func EncodeSImmediate(imm int64) (int64, error) {
1448if !immIFits(imm, 12) {
1449return 0, fmt.Errorf("immediate %#x does not fit in 12 bits", imm)
1450}
1451return ((imm >> 5) << 25) | ((imm & 0x1f) << 7), nil
1452}
1453
1454func EncodeUImmediate(imm int64) (int64, error) {
1455if !immIFits(imm, 20) {
1456return 0, fmt.Errorf("immediate %#x does not fit in 20 bits", imm)
1457}
1458return imm << 12, nil
1459}
1460
1461type encoding struct {
1462encode func(*instruction) uint32 // encode returns the machine code for an instruction
1463validate func(*obj.Link, *instruction) // validate validates an instruction
1464length int // length of encoded instruction; 0 for pseudo-ops, 4 otherwise
1465}
1466
1467var (
1468// Encodings have the following naming convention:
1469//
1470// 1. the instruction encoding (R/I/S/B/U/J), in lowercase
1471// 2. zero or more register operand identifiers (I = integer
1472// register, F = float register), in uppercase
1473// 3. the word "Encoding"
1474//
1475// For example, rIIIEncoding indicates an R-type instruction with two
1476// integer register inputs and an integer register output; sFEncoding
1477// indicates an S-type instruction with rs2 being a float register.
1478
1479rIIIEncoding = encoding{encode: encodeRIII, validate: validateRIII, length: 4}
1480rFFFEncoding = encoding{encode: encodeRFFF, validate: validateRFFF, length: 4}
1481rFFIEncoding = encoding{encode: encodeRFFI, validate: validateRFFI, length: 4}
1482rFIEncoding = encoding{encode: encodeRFI, validate: validateRFI, length: 4}
1483rIFEncoding = encoding{encode: encodeRIF, validate: validateRIF, length: 4}
1484rFFEncoding = encoding{encode: encodeRFF, validate: validateRFF, length: 4}
1485
1486iIEncoding = encoding{encode: encodeII, validate: validateII, length: 4}
1487iFEncoding = encoding{encode: encodeIF, validate: validateIF, length: 4}
1488
1489sIEncoding = encoding{encode: encodeSI, validate: validateSI, length: 4}
1490sFEncoding = encoding{encode: encodeSF, validate: validateSF, length: 4}
1491
1492bEncoding = encoding{encode: encodeB, validate: validateB, length: 4}
1493uEncoding = encoding{encode: encodeU, validate: validateU, length: 4}
1494jEncoding = encoding{encode: encodeJ, validate: validateJ, length: 4}
1495
1496// rawEncoding encodes a raw instruction byte sequence.
1497rawEncoding = encoding{encode: encodeRawIns, validate: validateRaw, length: 4}
1498
1499// pseudoOpEncoding panics if encoding is attempted, but does no validation.
1500pseudoOpEncoding = encoding{encode: nil, validate: func(*obj.Link, *instruction) {}, length: 0}
1501
1502// badEncoding is used when an invalid op is encountered.
1503// An error has already been generated, so let anything else through.
1504badEncoding = encoding{encode: func(*instruction) uint32 { return 0 }, validate: func(*obj.Link, *instruction) {}, length: 0}
1505)
1506
1507// encodings contains the encodings for RISC-V instructions.
1508// Instructions are masked with obj.AMask to keep indices small.
1509var encodings = [ALAST & obj.AMask]encoding{
1510
1511// Unprivileged ISA
1512
1513// 2.4: Integer Computational Instructions
1514AADDI & obj.AMask: iIEncoding,
1515ASLTI & obj.AMask: iIEncoding,
1516ASLTIU & obj.AMask: iIEncoding,
1517AANDI & obj.AMask: iIEncoding,
1518AORI & obj.AMask: iIEncoding,
1519AXORI & obj.AMask: iIEncoding,
1520ASLLI & obj.AMask: iIEncoding,
1521ASRLI & obj.AMask: iIEncoding,
1522ASRAI & obj.AMask: iIEncoding,
1523ALUI & obj.AMask: uEncoding,
1524AAUIPC & obj.AMask: uEncoding,
1525AADD & obj.AMask: rIIIEncoding,
1526ASLT & obj.AMask: rIIIEncoding,
1527ASLTU & obj.AMask: rIIIEncoding,
1528AAND & obj.AMask: rIIIEncoding,
1529AOR & obj.AMask: rIIIEncoding,
1530AXOR & obj.AMask: rIIIEncoding,
1531ASLL & obj.AMask: rIIIEncoding,
1532ASRL & obj.AMask: rIIIEncoding,
1533ASUB & obj.AMask: rIIIEncoding,
1534ASRA & obj.AMask: rIIIEncoding,
1535
1536// 2.5: Control Transfer Instructions
1537AJAL & obj.AMask: jEncoding,
1538AJALR & obj.AMask: iIEncoding,
1539ABEQ & obj.AMask: bEncoding,
1540ABNE & obj.AMask: bEncoding,
1541ABLT & obj.AMask: bEncoding,
1542ABLTU & obj.AMask: bEncoding,
1543ABGE & obj.AMask: bEncoding,
1544ABGEU & obj.AMask: bEncoding,
1545
1546// 2.6: Load and Store Instructions
1547ALW & obj.AMask: iIEncoding,
1548ALWU & obj.AMask: iIEncoding,
1549ALH & obj.AMask: iIEncoding,
1550ALHU & obj.AMask: iIEncoding,
1551ALB & obj.AMask: iIEncoding,
1552ALBU & obj.AMask: iIEncoding,
1553ASW & obj.AMask: sIEncoding,
1554ASH & obj.AMask: sIEncoding,
1555ASB & obj.AMask: sIEncoding,
1556
1557// 2.7: Memory Ordering
1558AFENCE & obj.AMask: iIEncoding,
1559
1560// 5.2: Integer Computational Instructions (RV64I)
1561AADDIW & obj.AMask: iIEncoding,
1562ASLLIW & obj.AMask: iIEncoding,
1563ASRLIW & obj.AMask: iIEncoding,
1564ASRAIW & obj.AMask: iIEncoding,
1565AADDW & obj.AMask: rIIIEncoding,
1566ASLLW & obj.AMask: rIIIEncoding,
1567ASRLW & obj.AMask: rIIIEncoding,
1568ASUBW & obj.AMask: rIIIEncoding,
1569ASRAW & obj.AMask: rIIIEncoding,
1570
1571// 5.3: Load and Store Instructions (RV64I)
1572ALD & obj.AMask: iIEncoding,
1573ASD & obj.AMask: sIEncoding,
1574
1575// 7.1: Multiplication Operations
1576AMUL & obj.AMask: rIIIEncoding,
1577AMULH & obj.AMask: rIIIEncoding,
1578AMULHU & obj.AMask: rIIIEncoding,
1579AMULHSU & obj.AMask: rIIIEncoding,
1580AMULW & obj.AMask: rIIIEncoding,
1581ADIV & obj.AMask: rIIIEncoding,
1582ADIVU & obj.AMask: rIIIEncoding,
1583AREM & obj.AMask: rIIIEncoding,
1584AREMU & obj.AMask: rIIIEncoding,
1585ADIVW & obj.AMask: rIIIEncoding,
1586ADIVUW & obj.AMask: rIIIEncoding,
1587AREMW & obj.AMask: rIIIEncoding,
1588AREMUW & obj.AMask: rIIIEncoding,
1589
1590// 8.2: Load-Reserved/Store-Conditional
1591ALRW & obj.AMask: rIIIEncoding,
1592ALRD & obj.AMask: rIIIEncoding,
1593ASCW & obj.AMask: rIIIEncoding,
1594ASCD & obj.AMask: rIIIEncoding,
1595
1596// 8.3: Atomic Memory Operations
1597AAMOSWAPW & obj.AMask: rIIIEncoding,
1598AAMOSWAPD & obj.AMask: rIIIEncoding,
1599AAMOADDW & obj.AMask: rIIIEncoding,
1600AAMOADDD & obj.AMask: rIIIEncoding,
1601AAMOANDW & obj.AMask: rIIIEncoding,
1602AAMOANDD & obj.AMask: rIIIEncoding,
1603AAMOORW & obj.AMask: rIIIEncoding,
1604AAMOORD & obj.AMask: rIIIEncoding,
1605AAMOXORW & obj.AMask: rIIIEncoding,
1606AAMOXORD & obj.AMask: rIIIEncoding,
1607AAMOMAXW & obj.AMask: rIIIEncoding,
1608AAMOMAXD & obj.AMask: rIIIEncoding,
1609AAMOMAXUW & obj.AMask: rIIIEncoding,
1610AAMOMAXUD & obj.AMask: rIIIEncoding,
1611AAMOMINW & obj.AMask: rIIIEncoding,
1612AAMOMIND & obj.AMask: rIIIEncoding,
1613AAMOMINUW & obj.AMask: rIIIEncoding,
1614AAMOMINUD & obj.AMask: rIIIEncoding,
1615
1616// 10.1: Base Counters and Timers
1617ARDCYCLE & obj.AMask: iIEncoding,
1618ARDTIME & obj.AMask: iIEncoding,
1619ARDINSTRET & obj.AMask: iIEncoding,
1620
1621// 11.5: Single-Precision Load and Store Instructions
1622AFLW & obj.AMask: iFEncoding,
1623AFSW & obj.AMask: sFEncoding,
1624
1625// 11.6: Single-Precision Floating-Point Computational Instructions
1626AFADDS & obj.AMask: rFFFEncoding,
1627AFSUBS & obj.AMask: rFFFEncoding,
1628AFMULS & obj.AMask: rFFFEncoding,
1629AFDIVS & obj.AMask: rFFFEncoding,
1630AFMINS & obj.AMask: rFFFEncoding,
1631AFMAXS & obj.AMask: rFFFEncoding,
1632AFSQRTS & obj.AMask: rFFFEncoding,
1633
1634// 11.7: Single-Precision Floating-Point Conversion and Move Instructions
1635AFCVTWS & obj.AMask: rFIEncoding,
1636AFCVTLS & obj.AMask: rFIEncoding,
1637AFCVTSW & obj.AMask: rIFEncoding,
1638AFCVTSL & obj.AMask: rIFEncoding,
1639AFCVTWUS & obj.AMask: rFIEncoding,
1640AFCVTLUS & obj.AMask: rFIEncoding,
1641AFCVTSWU & obj.AMask: rIFEncoding,
1642AFCVTSLU & obj.AMask: rIFEncoding,
1643AFSGNJS & obj.AMask: rFFFEncoding,
1644AFSGNJNS & obj.AMask: rFFFEncoding,
1645AFSGNJXS & obj.AMask: rFFFEncoding,
1646AFMVXS & obj.AMask: rFIEncoding,
1647AFMVSX & obj.AMask: rIFEncoding,
1648AFMVXW & obj.AMask: rFIEncoding,
1649AFMVWX & obj.AMask: rIFEncoding,
1650
1651// 11.8: Single-Precision Floating-Point Compare Instructions
1652AFEQS & obj.AMask: rFFIEncoding,
1653AFLTS & obj.AMask: rFFIEncoding,
1654AFLES & obj.AMask: rFFIEncoding,
1655
1656// 11.9: Single-Precision Floating-Point Classify Instruction
1657AFCLASSS & obj.AMask: rFIEncoding,
1658
1659// 12.3: Double-Precision Load and Store Instructions
1660AFLD & obj.AMask: iFEncoding,
1661AFSD & obj.AMask: sFEncoding,
1662
1663// 12.4: Double-Precision Floating-Point Computational Instructions
1664AFADDD & obj.AMask: rFFFEncoding,
1665AFSUBD & obj.AMask: rFFFEncoding,
1666AFMULD & obj.AMask: rFFFEncoding,
1667AFDIVD & obj.AMask: rFFFEncoding,
1668AFMIND & obj.AMask: rFFFEncoding,
1669AFMAXD & obj.AMask: rFFFEncoding,
1670AFSQRTD & obj.AMask: rFFFEncoding,
1671
1672// 12.5: Double-Precision Floating-Point Conversion and Move Instructions
1673AFCVTWD & obj.AMask: rFIEncoding,
1674AFCVTLD & obj.AMask: rFIEncoding,
1675AFCVTDW & obj.AMask: rIFEncoding,
1676AFCVTDL & obj.AMask: rIFEncoding,
1677AFCVTWUD & obj.AMask: rFIEncoding,
1678AFCVTLUD & obj.AMask: rFIEncoding,
1679AFCVTDWU & obj.AMask: rIFEncoding,
1680AFCVTDLU & obj.AMask: rIFEncoding,
1681AFCVTSD & obj.AMask: rFFEncoding,
1682AFCVTDS & obj.AMask: rFFEncoding,
1683AFSGNJD & obj.AMask: rFFFEncoding,
1684AFSGNJND & obj.AMask: rFFFEncoding,
1685AFSGNJXD & obj.AMask: rFFFEncoding,
1686AFMVXD & obj.AMask: rFIEncoding,
1687AFMVDX & obj.AMask: rIFEncoding,
1688
1689// 12.6: Double-Precision Floating-Point Compare Instructions
1690AFEQD & obj.AMask: rFFIEncoding,
1691AFLTD & obj.AMask: rFFIEncoding,
1692AFLED & obj.AMask: rFFIEncoding,
1693
1694// 12.7: Double-Precision Floating-Point Classify Instruction
1695AFCLASSD & obj.AMask: rFIEncoding,
1696
1697// Privileged ISA
1698
1699// 3.2.1: Environment Call and Breakpoint
1700AECALL & obj.AMask: iIEncoding,
1701AEBREAK & obj.AMask: iIEncoding,
1702
1703// Escape hatch
1704AWORD & obj.AMask: rawEncoding,
1705
1706// Pseudo-operations
1707obj.AFUNCDATA: pseudoOpEncoding,
1708obj.APCDATA: pseudoOpEncoding,
1709obj.ATEXT: pseudoOpEncoding,
1710obj.ANOP: pseudoOpEncoding,
1711}
1712
1713// encodingForAs returns the encoding for an obj.As.
1714func encodingForAs(as obj.As) (encoding, error) {
1715if base := as &^ obj.AMask; base != obj.ABaseRISCV && base != 0 {
1716return badEncoding, fmt.Errorf("encodingForAs: not a RISC-V instruction %s", as)
1717}
1718asi := as & obj.AMask
1719if int(asi) >= len(encodings) {
1720return badEncoding, fmt.Errorf("encodingForAs: bad RISC-V instruction %s", as)
1721}
1722enc := encodings[asi]
1723if enc.validate == nil {
1724return badEncoding, fmt.Errorf("encodingForAs: no encoding for instruction %s", as)
1725}
1726return enc, nil
1727}
1728
1729type instruction struct {
1730as obj.As // Assembler opcode
1731rd uint32 // Destination register
1732rs1 uint32 // Source register 1
1733rs2 uint32 // Source register 2
1734imm int64 // Immediate
1735funct3 uint32 // Function 3
1736funct7 uint32 // Function 7
1737}
1738
1739func (ins *instruction) encode() (uint32, error) {
1740enc, err := encodingForAs(ins.as)
1741if err != nil {
1742return 0, err
1743}
1744if enc.length > 0 {
1745return enc.encode(ins), nil
1746}
1747return 0, fmt.Errorf("fixme")
1748}
1749
1750func (ins *instruction) length() int {
1751enc, err := encodingForAs(ins.as)
1752if err != nil {
1753return 0
1754}
1755return enc.length
1756}
1757
1758func (ins *instruction) validate(ctxt *obj.Link) {
1759enc, err := encodingForAs(ins.as)
1760if err != nil {
1761ctxt.Diag(err.Error())
1762return
1763}
1764enc.validate(ctxt, ins)
1765}
1766
1767// instructionsForProg returns the machine instructions for an *obj.Prog.
1768func instructionsForProg(p *obj.Prog) []*instruction {
1769ins := &instruction{
1770as: p.As,
1771rd: uint32(p.To.Reg),
1772rs1: uint32(p.Reg),
1773rs2: uint32(p.From.Reg),
1774imm: p.From.Offset,
1775}
1776
1777inss := []*instruction{ins}
1778switch ins.as {
1779case AJAL, AJALR:
1780ins.rd, ins.rs1, ins.rs2 = uint32(p.From.Reg), uint32(p.To.Reg), obj.REG_NONE
1781ins.imm = p.To.Offset
1782
1783case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ:
1784switch ins.as {
1785case ABEQZ:
1786ins.as, ins.rs1, ins.rs2 = ABEQ, REG_ZERO, uint32(p.From.Reg)
1787case ABGEZ:
1788ins.as, ins.rs1, ins.rs2 = ABGE, REG_ZERO, uint32(p.From.Reg)
1789case ABGT:
1790ins.as, ins.rs1, ins.rs2 = ABLT, uint32(p.Reg), uint32(p.From.Reg)
1791case ABGTU:
1792ins.as, ins.rs1, ins.rs2 = ABLTU, uint32(p.Reg), uint32(p.From.Reg)
1793case ABGTZ:
1794ins.as, ins.rs1, ins.rs2 = ABLT, uint32(p.From.Reg), REG_ZERO
1795case ABLE:
1796ins.as, ins.rs1, ins.rs2 = ABGE, uint32(p.Reg), uint32(p.From.Reg)
1797case ABLEU:
1798ins.as, ins.rs1, ins.rs2 = ABGEU, uint32(p.Reg), uint32(p.From.Reg)
1799case ABLEZ:
1800ins.as, ins.rs1, ins.rs2 = ABGE, uint32(p.From.Reg), REG_ZERO
1801case ABLTZ:
1802ins.as, ins.rs1, ins.rs2 = ABLT, REG_ZERO, uint32(p.From.Reg)
1803case ABNEZ:
1804ins.as, ins.rs1, ins.rs2 = ABNE, REG_ZERO, uint32(p.From.Reg)
1805}
1806ins.imm = p.To.Offset
1807
1808case ALW, ALWU, ALH, ALHU, ALB, ALBU, ALD, AFLW, AFLD:
1809if p.From.Type != obj.TYPE_MEM {
1810p.Ctxt.Diag("%v requires memory for source", p)
1811return nil
1812}
1813ins.rs1, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE
1814ins.imm = p.From.Offset
1815
1816case ASW, ASH, ASB, ASD, AFSW, AFSD:
1817if p.To.Type != obj.TYPE_MEM {
1818p.Ctxt.Diag("%v requires memory for destination", p)
1819return nil
1820}
1821ins.rs1, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE
1822ins.imm = p.To.Offset
1823
1824case ALRW, ALRD:
1825// Set aq to use acquire access ordering, which matches Go's memory requirements.
1826ins.funct7 = 2
1827ins.rs1, ins.rs2 = uint32(p.From.Reg), REG_ZERO
1828
1829case ASCW, ASCD, AAMOSWAPW, AAMOSWAPD, AAMOADDW, AAMOADDD, AAMOANDW, AAMOANDD, AAMOORW, AAMOORD,
1830AAMOXORW, AAMOXORD, AAMOMINW, AAMOMIND, AAMOMINUW, AAMOMINUD, AAMOMAXW, AAMOMAXD, AAMOMAXUW, AAMOMAXUD:
1831// Set aq to use acquire access ordering, which matches Go's memory requirements.
1832ins.funct7 = 2
1833ins.rd, ins.rs1, ins.rs2 = uint32(p.RegTo2), uint32(p.To.Reg), uint32(p.From.Reg)
1834
1835case AECALL, AEBREAK, ARDCYCLE, ARDTIME, ARDINSTRET:
1836insEnc := encode(p.As)
1837if p.To.Type == obj.TYPE_NONE {
1838ins.rd = REG_ZERO
1839}
1840ins.rs1 = REG_ZERO
1841ins.imm = insEnc.csr
1842
1843case AFENCE:
1844ins.rd, ins.rs1, ins.rs2 = REG_ZERO, REG_ZERO, obj.REG_NONE
1845ins.imm = 0x0ff
1846
1847case AFCVTWS, AFCVTLS, AFCVTWUS, AFCVTLUS, AFCVTWD, AFCVTLD, AFCVTWUD, AFCVTLUD:
1848// Set the rounding mode in funct3 to round to zero.
1849ins.funct3 = 1
1850
1851case AFNES, AFNED:
1852// Replace FNE[SD] with FEQ[SD] and NOT.
1853if p.To.Type != obj.TYPE_REG {
1854p.Ctxt.Diag("%v needs an integer register output", ins.as)
1855return nil
1856}
1857if ins.as == AFNES {
1858ins.as = AFEQS
1859} else {
1860ins.as = AFEQD
1861}
1862ins = &instruction{
1863as: AXORI, // [bit] xor 1 = not [bit]
1864rd: ins.rd,
1865rs1: ins.rd,
1866imm: 1,
1867}
1868inss = append(inss, ins)
1869
1870case AFSQRTS, AFSQRTD:
1871// These instructions expect a zero (i.e. float register 0)
1872// to be the second input operand.
1873ins.rs1 = uint32(p.From.Reg)
1874ins.rs2 = REG_F0
1875
1876case ANEG, ANEGW:
1877// NEG rs, rd -> SUB rs, X0, rd
1878ins.as = ASUB
1879if p.As == ANEGW {
1880ins.as = ASUBW
1881}
1882ins.rs1 = REG_ZERO
1883if ins.rd == obj.REG_NONE {
1884ins.rd = ins.rs2
1885}
1886
1887case ANOT:
1888// NOT rs, rd -> XORI $-1, rs, rd
1889ins.as = AXORI
1890ins.rs1, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE
1891if ins.rd == obj.REG_NONE {
1892ins.rd = ins.rs1
1893}
1894ins.imm = -1
1895
1896case ASEQZ:
1897// SEQZ rs, rd -> SLTIU $1, rs, rd
1898ins.as = ASLTIU
1899ins.rs1 = uint32(p.From.Reg)
1900ins.imm = 1
1901
1902case ASNEZ:
1903// SNEZ rs, rd -> SLTU rs, x0, rd
1904ins.as = ASLTU
1905ins.rs1 = REG_ZERO
1906
1907case AFNEGS:
1908// FNEGS rs, rd -> FSGNJNS rs, rs, rd
1909ins.as = AFSGNJNS
1910ins.rs1 = uint32(p.From.Reg)
1911
1912case AFNEGD:
1913// FNEGD rs, rd -> FSGNJND rs, rs, rd
1914ins.as = AFSGNJND
1915ins.rs1 = uint32(p.From.Reg)
1916}
1917return inss
1918}
1919
1920// assemble emits machine code.
1921// It is called at the very end of the assembly process.
1922func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
1923if ctxt.Retpoline {
1924ctxt.Diag("-spectre=ret not supported on riscv")
1925ctxt.Retpoline = false // don't keep printing
1926}
1927
1928var symcode []uint32
1929for p := cursym.Func.Text; p != nil; p = p.Link {
1930switch p.As {
1931case AJALR:
1932if p.To.Sym != nil {
1933// This is a CALL/JMP. We add a relocation only
1934// for linker stack checking. No actual
1935// relocation is needed.
1936rel := obj.Addrel(cursym)
1937rel.Off = int32(p.Pc)
1938rel.Siz = 4
1939rel.Sym = p.To.Sym
1940rel.Add = p.To.Offset
1941rel.Type = objabi.R_CALLRISCV
1942}
1943case AAUIPC:
1944var rt objabi.RelocType
1945if p.Mark&NEED_PCREL_ITYPE_RELOC == NEED_PCREL_ITYPE_RELOC {
1946rt = objabi.R_RISCV_PCREL_ITYPE
1947} else if p.Mark&NEED_PCREL_STYPE_RELOC == NEED_PCREL_STYPE_RELOC {
1948rt = objabi.R_RISCV_PCREL_STYPE
1949} else {
1950break
1951}
1952if p.Link == nil {
1953ctxt.Diag("AUIPC needing PC-relative reloc missing following instruction")
1954break
1955}
1956addr := p.RestArgs[0]
1957if addr.Sym == nil {
1958ctxt.Diag("AUIPC needing PC-relative reloc missing symbol")
1959break
1960}
1961
1962rel := obj.Addrel(cursym)
1963rel.Off = int32(p.Pc)
1964rel.Siz = 8
1965rel.Sym = addr.Sym
1966rel.Add = addr.Offset
1967rel.Type = rt
1968}
1969
1970for _, ins := range instructionsForProg(p) {
1971ic, err := ins.encode()
1972if err == nil {
1973symcode = append(symcode, ic)
1974}
1975}
1976}
1977cursym.Size = int64(4 * len(symcode))
1978
1979cursym.Grow(cursym.Size)
1980for p, i := cursym.P, 0; i < len(symcode); p, i = p[4:], i+1 {
1981ctxt.Arch.ByteOrder.PutUint32(p, symcode[i])
1982}
1983
1984obj.MarkUnsafePoints(ctxt, cursym.Func.Text, newprog, isUnsafePoint, nil)
1985}
1986
1987func isUnsafePoint(p *obj.Prog) bool {
1988return p.From.Reg == REG_TMP || p.To.Reg == REG_TMP || p.Reg == REG_TMP
1989}
1990
1991var LinkRISCV64 = obj.LinkArch{
1992Arch: sys.ArchRISCV64,
1993Init: buildop,
1994Preprocess: preprocess,
1995Assemble: assemble,
1996Progedit: progedit,
1997UnaryDst: unaryDst,
1998DWARFRegisters: RISCV64DWARFRegisters,
1999}
2000