1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999 |
- // Copyright © 2015 The Go Authors. All rights reserved.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- package riscv
- import (
- "github.com/twitchyliquid64/golang-asm/obj"
- "github.com/twitchyliquid64/golang-asm/objabi"
- "github.com/twitchyliquid64/golang-asm/sys"
- "fmt"
- )
- func buildop(ctxt *obj.Link) {}
- // jalrToSym replaces p with a set of Progs needed to jump to the Sym in p.
- // lr is the link register to use for the JALR.
- // p must be a CALL, JMP or RET.
- func jalrToSym(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc, lr int16) *obj.Prog {
- if p.As != obj.ACALL && p.As != obj.AJMP && p.As != obj.ARET {
- ctxt.Diag("unexpected Prog in jalrToSym: %v", p)
- return p
- }
- // TODO(jsing): Consider using a single JAL instruction and teaching
- // the linker to provide trampolines for the case where the destination
- // offset is too large. This would potentially reduce instructions for
- // the common case, but would require three instructions to go via the
- // trampoline.
- to := p.To
- p.As = AAUIPC
- p.Mark |= NEED_PCREL_ITYPE_RELOC
- p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: to.Offset, Sym: to.Sym}}
- p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
- p.Reg = 0
- p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
- p = obj.Appendp(p, newprog)
- // Leave Sym only for the CALL reloc in assemble.
- p.As = AJALR
- p.From.Type = obj.TYPE_REG
- p.From.Reg = lr
- p.Reg = 0
- p.To.Type = obj.TYPE_REG
- p.To.Reg = REG_TMP
- p.To.Sym = to.Sym
- return p
- }
- // progedit is called individually for each *obj.Prog. It normalizes instruction
- // formats and eliminates as many pseudo-instructions as possible.
- func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
- // Expand binary instructions to ternary ones.
- if p.Reg == 0 {
- switch p.As {
- case AADDI, ASLTI, ASLTIU, AANDI, AORI, AXORI, ASLLI, ASRLI, ASRAI,
- AADD, AAND, AOR, AXOR, ASLL, ASRL, ASUB, ASRA,
- AMUL, AMULH, AMULHU, AMULHSU, AMULW, ADIV, ADIVU, ADIVW, ADIVUW,
- AREM, AREMU, AREMW, AREMUW:
- p.Reg = p.To.Reg
- }
- }
- // Rewrite instructions with constant operands to refer to the immediate
- // form of the instruction.
- if p.From.Type == obj.TYPE_CONST {
- switch p.As {
- case AADD:
- p.As = AADDI
- case ASLT:
- p.As = ASLTI
- case ASLTU:
- p.As = ASLTIU
- case AAND:
- p.As = AANDI
- case AOR:
- p.As = AORI
- case AXOR:
- p.As = AXORI
- case ASLL:
- p.As = ASLLI
- case ASRL:
- p.As = ASRLI
- case ASRA:
- p.As = ASRAI
- }
- }
- switch p.As {
- case obj.AJMP:
- // Turn JMP into JAL ZERO or JALR ZERO.
- p.From.Type = obj.TYPE_REG
- p.From.Reg = REG_ZERO
- switch p.To.Type {
- case obj.TYPE_BRANCH:
- p.As = AJAL
- case obj.TYPE_MEM:
- switch p.To.Name {
- case obj.NAME_NONE:
- p.As = AJALR
- case obj.NAME_EXTERN:
- // Handled in preprocess.
- default:
- ctxt.Diag("unsupported name %d for %v", p.To.Name, p)
- }
- default:
- panic(fmt.Sprintf("unhandled type %+v", p.To.Type))
- }
- case obj.ACALL:
- switch p.To.Type {
- case obj.TYPE_MEM:
- // Handled in preprocess.
- case obj.TYPE_REG:
- p.As = AJALR
- p.From.Type = obj.TYPE_REG
- p.From.Reg = REG_LR
- default:
- ctxt.Diag("unknown destination type %+v in CALL: %v", p.To.Type, p)
- }
- case obj.AUNDEF:
- p.As = AEBREAK
- case ASCALL:
- // SCALL is the old name for ECALL.
- p.As = AECALL
- case ASBREAK:
- // SBREAK is the old name for EBREAK.
- p.As = AEBREAK
- }
- }
- // addrToReg extracts the register from an Addr, handling special Addr.Names.
- func addrToReg(a obj.Addr) int16 {
- switch a.Name {
- case obj.NAME_PARAM, obj.NAME_AUTO:
- return REG_SP
- }
- return a.Reg
- }
- // movToLoad converts a MOV mnemonic into the corresponding load instruction.
- func movToLoad(mnemonic obj.As) obj.As {
- switch mnemonic {
- case AMOV:
- return ALD
- case AMOVB:
- return ALB
- case AMOVH:
- return ALH
- case AMOVW:
- return ALW
- case AMOVBU:
- return ALBU
- case AMOVHU:
- return ALHU
- case AMOVWU:
- return ALWU
- case AMOVF:
- return AFLW
- case AMOVD:
- return AFLD
- default:
- panic(fmt.Sprintf("%+v is not a MOV", mnemonic))
- }
- }
- // movToStore converts a MOV mnemonic into the corresponding store instruction.
- func movToStore(mnemonic obj.As) obj.As {
- switch mnemonic {
- case AMOV:
- return ASD
- case AMOVB:
- return ASB
- case AMOVH:
- return ASH
- case AMOVW:
- return ASW
- case AMOVF:
- return AFSW
- case AMOVD:
- return AFSD
- default:
- panic(fmt.Sprintf("%+v is not a MOV", mnemonic))
- }
- }
- // rewriteMOV rewrites MOV pseudo-instructions.
- func rewriteMOV(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog) {
- switch p.As {
- case AMOV, AMOVB, AMOVH, AMOVW, AMOVBU, AMOVHU, AMOVWU, AMOVF, AMOVD:
- default:
- panic(fmt.Sprintf("%+v is not a MOV pseudo-instruction", p.As))
- }
- switch p.From.Type {
- case obj.TYPE_MEM: // MOV c(Rs), Rd -> L $c, Rs, Rd
- switch p.From.Name {
- case obj.NAME_AUTO, obj.NAME_PARAM, obj.NAME_NONE:
- if p.To.Type != obj.TYPE_REG {
- ctxt.Diag("unsupported load at %v", p)
- }
- p.As = movToLoad(p.As)
- p.From.Reg = addrToReg(p.From)
- case obj.NAME_EXTERN, obj.NAME_STATIC:
- // AUIPC $off_hi, R
- // L $off_lo, R
- as := p.As
- to := p.To
- p.As = AAUIPC
- p.Mark |= NEED_PCREL_ITYPE_RELOC
- p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: p.From.Offset, Sym: p.From.Sym}}
- p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
- p.Reg = 0
- p.To = obj.Addr{Type: obj.TYPE_REG, Reg: to.Reg}
- p = obj.Appendp(p, newprog)
- p.As = movToLoad(as)
- p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: to.Reg, Offset: 0}
- p.To = to
- default:
- ctxt.Diag("unsupported name %d for %v", p.From.Name, p)
- }
- case obj.TYPE_REG:
- switch p.To.Type {
- case obj.TYPE_REG:
- switch p.As {
- case AMOV: // MOV Ra, Rb -> ADDI $0, Ra, Rb
- p.As = AADDI
- p.Reg = p.From.Reg
- p.From = obj.Addr{Type: obj.TYPE_CONST}
- case AMOVF: // MOVF Ra, Rb -> FSGNJS Ra, Ra, Rb
- p.As = AFSGNJS
- p.Reg = p.From.Reg
- case AMOVD: // MOVD Ra, Rb -> FSGNJD Ra, Ra, Rb
- p.As = AFSGNJD
- p.Reg = p.From.Reg
- default:
- ctxt.Diag("unsupported register-register move at %v", p)
- }
- case obj.TYPE_MEM: // MOV Rs, c(Rd) -> S $c, Rs, Rd
- switch p.As {
- case AMOVBU, AMOVHU, AMOVWU:
- ctxt.Diag("unsupported unsigned store at %v", p)
- }
- switch p.To.Name {
- case obj.NAME_AUTO, obj.NAME_PARAM, obj.NAME_NONE:
- p.As = movToStore(p.As)
- p.To.Reg = addrToReg(p.To)
- case obj.NAME_EXTERN:
- // AUIPC $off_hi, TMP
- // S $off_lo, TMP, R
- as := p.As
- from := p.From
- p.As = AAUIPC
- p.Mark |= NEED_PCREL_STYPE_RELOC
- p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: p.To.Offset, Sym: p.To.Sym}}
- p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
- p.Reg = 0
- p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
- p = obj.Appendp(p, newprog)
- p.As = movToStore(as)
- p.From = from
- p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_TMP, Offset: 0}
- default:
- ctxt.Diag("unsupported name %d for %v", p.From.Name, p)
- }
- default:
- ctxt.Diag("unsupported MOV at %v", p)
- }
- case obj.TYPE_CONST:
- // MOV $c, R
- // If c is small enough, convert to:
- // ADD $c, ZERO, R
- // If not, convert to:
- // LUI top20bits(c), R
- // ADD bottom12bits(c), R, R
- if p.As != AMOV {
- ctxt.Diag("unsupported constant load at %v", p)
- }
- off := p.From.Offset
- to := p.To
- low, high, err := Split32BitImmediate(off)
- if err != nil {
- ctxt.Diag("%v: constant %d too large: %v", p, off, err)
- }
- // LUI is only necessary if the offset doesn't fit in 12-bits.
- needLUI := high != 0
- if needLUI {
- p.As = ALUI
- p.To = to
- // Pass top 20 bits to LUI.
- p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: high}
- p = obj.Appendp(p, newprog)
- }
- p.As = AADDIW
- p.To = to
- p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: low}
- p.Reg = REG_ZERO
- if needLUI {
- p.Reg = to.Reg
- }
- case obj.TYPE_ADDR: // MOV $sym+off(SP/SB), R
- if p.To.Type != obj.TYPE_REG || p.As != AMOV {
- ctxt.Diag("unsupported addr MOV at %v", p)
- }
- switch p.From.Name {
- case obj.NAME_EXTERN, obj.NAME_STATIC:
- // AUIPC $off_hi, R
- // ADDI $off_lo, R
- to := p.To
- p.As = AAUIPC
- p.Mark |= NEED_PCREL_ITYPE_RELOC
- p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: p.From.Offset, Sym: p.From.Sym}}
- p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
- p.Reg = 0
- p.To = to
- p = obj.Appendp(p, newprog)
- p.As = AADDI
- p.From = obj.Addr{Type: obj.TYPE_CONST}
- p.Reg = to.Reg
- p.To = to
- case obj.NAME_PARAM, obj.NAME_AUTO:
- p.As = AADDI
- p.Reg = REG_SP
- p.From.Type = obj.TYPE_CONST
- case obj.NAME_NONE:
- p.As = AADDI
- p.Reg = p.From.Reg
- p.From.Type = obj.TYPE_CONST
- p.From.Reg = 0
- default:
- ctxt.Diag("bad addr MOV from name %v at %v", p.From.Name, p)
- }
- default:
- ctxt.Diag("unsupported MOV at %v", p)
- }
- }
- // InvertBranch inverts the condition of a conditional branch.
- func InvertBranch(as obj.As) obj.As {
- switch as {
- case ABEQ:
- return ABNE
- case ABEQZ:
- return ABNEZ
- case ABGE:
- return ABLT
- case ABGEU:
- return ABLTU
- case ABGEZ:
- return ABLTZ
- case ABGT:
- return ABLE
- case ABGTU:
- return ABLEU
- case ABGTZ:
- return ABLEZ
- case ABLE:
- return ABGT
- case ABLEU:
- return ABGTU
- case ABLEZ:
- return ABGTZ
- case ABLT:
- return ABGE
- case ABLTU:
- return ABGEU
- case ABLTZ:
- return ABGEZ
- case ABNE:
- return ABEQ
- case ABNEZ:
- return ABEQZ
- default:
- panic("InvertBranch: not a branch")
- }
- }
- // containsCall reports whether the symbol contains a CALL (or equivalent)
- // instruction. Must be called after progedit.
- func containsCall(sym *obj.LSym) bool {
- // CALLs are CALL or JAL(R) with link register LR.
- for p := sym.Func.Text; p != nil; p = p.Link {
- switch p.As {
- case obj.ACALL:
- return true
- case AJAL, AJALR:
- if p.From.Type == obj.TYPE_REG && p.From.Reg == REG_LR {
- return true
- }
- }
- }
- return false
- }
- // setPCs sets the Pc field in all instructions reachable from p.
- // It uses pc as the initial value.
- func setPCs(p *obj.Prog, pc int64) {
- for ; p != nil; p = p.Link {
- p.Pc = pc
- for _, ins := range instructionsForProg(p) {
- pc += int64(ins.length())
- }
- }
- }
- // stackOffset updates Addr offsets based on the current stack size.
- //
- // The stack looks like:
- // -------------------
- // | |
- // | PARAMs |
- // | |
- // | |
- // -------------------
- // | Parent RA | SP on function entry
- // -------------------
- // | |
- // | |
- // | AUTOs |
- // | |
- // | |
- // -------------------
- // | RA | SP during function execution
- // -------------------
- //
- // FixedFrameSize makes other packages aware of the space allocated for RA.
- //
- // A nicer version of this diagram can be found on slide 21 of the presentation
- // attached to:
- //
- // https://golang.org/issue/16922#issuecomment-243748180
- //
- func stackOffset(a *obj.Addr, stacksize int64) {
- switch a.Name {
- case obj.NAME_AUTO:
- // Adjust to the top of AUTOs.
- a.Offset += stacksize
- case obj.NAME_PARAM:
- // Adjust to the bottom of PARAMs.
- a.Offset += stacksize + 8
- }
- }
- // preprocess generates prologue and epilogue code, computes PC-relative branch
- // and jump offsets, and resolves pseudo-registers.
- //
- // preprocess is called once per linker symbol.
- //
- // When preprocess finishes, all instructions in the symbol are either
- // concrete, real RISC-V instructions or directive pseudo-ops like TEXT,
- // PCDATA, and FUNCDATA.
- func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
- if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
- return
- }
- // Generate the prologue.
- text := cursym.Func.Text
- if text.As != obj.ATEXT {
- ctxt.Diag("preprocess: found symbol that does not start with TEXT directive")
- return
- }
- stacksize := text.To.Offset
- if stacksize == -8 {
- // Historical way to mark NOFRAME.
- text.From.Sym.Set(obj.AttrNoFrame, true)
- stacksize = 0
- }
- if stacksize < 0 {
- ctxt.Diag("negative frame size %d - did you mean NOFRAME?", stacksize)
- }
- if text.From.Sym.NoFrame() {
- if stacksize != 0 {
- ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", stacksize)
- }
- }
- if !containsCall(cursym) {
- text.From.Sym.Set(obj.AttrLeaf, true)
- if stacksize == 0 {
- // A leaf function with no locals has no frame.
- text.From.Sym.Set(obj.AttrNoFrame, true)
- }
- }
- // Save LR unless there is no frame.
- if !text.From.Sym.NoFrame() {
- stacksize += ctxt.FixedFrameSize()
- }
- cursym.Func.Args = text.To.Val.(int32)
- cursym.Func.Locals = int32(stacksize)
- prologue := text
- if !cursym.Func.Text.From.Sym.NoSplit() {
- prologue = stacksplit(ctxt, prologue, cursym, newprog, stacksize) // emit split check
- }
- if stacksize != 0 {
- prologue = ctxt.StartUnsafePoint(prologue, newprog)
- // Actually save LR.
- prologue = obj.Appendp(prologue, newprog)
- prologue.As = AMOV
- prologue.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
- prologue.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: -stacksize}
- // Insert stack adjustment.
- prologue = obj.Appendp(prologue, newprog)
- prologue.As = AADDI
- prologue.From = obj.Addr{Type: obj.TYPE_CONST, Offset: -stacksize}
- prologue.Reg = REG_SP
- prologue.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_SP}
- prologue.Spadj = int32(stacksize)
- prologue = ctxt.EndUnsafePoint(prologue, newprog, -1)
- }
- if cursym.Func.Text.From.Sym.Wrapper() {
- // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
- //
- // MOV g_panic(g), X11
- // BNE X11, ZERO, adjust
- // end:
- // NOP
- // ...rest of function..
- // adjust:
- // MOV panic_argp(X11), X12
- // ADD $(autosize+FIXED_FRAME), SP, X13
- // BNE X12, X13, end
- // ADD $FIXED_FRAME, SP, X12
- // MOV X12, panic_argp(X11)
- // JMP end
- //
- // The NOP is needed to give the jumps somewhere to land.
- ldpanic := obj.Appendp(prologue, newprog)
- ldpanic.As = AMOV
- ldpanic.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REGG, Offset: 4 * int64(ctxt.Arch.PtrSize)} // G.panic
- ldpanic.Reg = 0
- ldpanic.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X11}
- bneadj := obj.Appendp(ldpanic, newprog)
- bneadj.As = ABNE
- bneadj.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X11}
- bneadj.Reg = REG_ZERO
- bneadj.To.Type = obj.TYPE_BRANCH
- endadj := obj.Appendp(bneadj, newprog)
- endadj.As = obj.ANOP
- last := endadj
- for last.Link != nil {
- last = last.Link
- }
- getargp := obj.Appendp(last, newprog)
- getargp.As = AMOV
- getargp.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_X11, Offset: 0} // Panic.argp
- getargp.Reg = 0
- getargp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X12}
- bneadj.To.SetTarget(getargp)
- calcargp := obj.Appendp(getargp, newprog)
- calcargp.As = AADDI
- calcargp.From = obj.Addr{Type: obj.TYPE_CONST, Offset: stacksize + ctxt.FixedFrameSize()}
- calcargp.Reg = REG_SP
- calcargp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X13}
- testargp := obj.Appendp(calcargp, newprog)
- testargp.As = ABNE
- testargp.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X12}
- testargp.Reg = REG_X13
- testargp.To.Type = obj.TYPE_BRANCH
- testargp.To.SetTarget(endadj)
- adjargp := obj.Appendp(testargp, newprog)
- adjargp.As = AADDI
- adjargp.From = obj.Addr{Type: obj.TYPE_CONST, Offset: int64(ctxt.Arch.PtrSize)}
- adjargp.Reg = REG_SP
- adjargp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X12}
- setargp := obj.Appendp(adjargp, newprog)
- setargp.As = AMOV
- setargp.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X12}
- setargp.Reg = 0
- setargp.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_X11, Offset: 0} // Panic.argp
- godone := obj.Appendp(setargp, newprog)
- godone.As = AJAL
- godone.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO}
- godone.To.Type = obj.TYPE_BRANCH
- godone.To.SetTarget(endadj)
- }
- // Update stack-based offsets.
- for p := cursym.Func.Text; p != nil; p = p.Link {
- stackOffset(&p.From, stacksize)
- stackOffset(&p.To, stacksize)
- }
- // Additional instruction rewriting.
- for p := cursym.Func.Text; p != nil; p = p.Link {
- switch p.As {
- case obj.AGETCALLERPC:
- if cursym.Leaf() {
- // MOV LR, Rd
- p.As = AMOV
- p.From.Type = obj.TYPE_REG
- p.From.Reg = REG_LR
- } else {
- // MOV (RSP), Rd
- p.As = AMOV
- p.From.Type = obj.TYPE_MEM
- p.From.Reg = REG_SP
- }
- case obj.ACALL:
- switch p.To.Type {
- case obj.TYPE_MEM:
- jalrToSym(ctxt, p, newprog, REG_LR)
- }
- case obj.AJMP:
- switch p.To.Type {
- case obj.TYPE_MEM:
- switch p.To.Name {
- case obj.NAME_EXTERN:
- // JMP to symbol.
- jalrToSym(ctxt, p, newprog, REG_ZERO)
- }
- }
- case obj.ARET:
- // Replace RET with epilogue.
- retJMP := p.To.Sym
- if stacksize != 0 {
- // Restore LR.
- p.As = AMOV
- p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: 0}
- p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
- p = obj.Appendp(p, newprog)
- p.As = AADDI
- p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: stacksize}
- p.Reg = REG_SP
- p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_SP}
- p.Spadj = int32(-stacksize)
- p = obj.Appendp(p, newprog)
- }
- if retJMP != nil {
- p.As = obj.ARET
- p.To.Sym = retJMP
- p = jalrToSym(ctxt, p, newprog, REG_ZERO)
- } else {
- p.As = AJALR
- p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO}
- p.Reg = 0
- p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
- }
- // "Add back" the stack removed in the previous instruction.
- //
- // This is to avoid confusing pctospadj, which sums
- // Spadj from function entry to each PC, and shouldn't
- // count adjustments from earlier epilogues, since they
- // won't affect later PCs.
- p.Spadj = int32(stacksize)
- case AADDI:
- // Refine Spadjs account for adjustment via ADDI instruction.
- if p.To.Type == obj.TYPE_REG && p.To.Reg == REG_SP && p.From.Type == obj.TYPE_CONST {
- p.Spadj = int32(-p.From.Offset)
- }
- }
- }
- // Rewrite MOV pseudo-instructions. This cannot be done in
- // progedit, as SP offsets need to be applied before we split
- // up some of the Addrs.
- for p := cursym.Func.Text; p != nil; p = p.Link {
- switch p.As {
- case AMOV, AMOVB, AMOVH, AMOVW, AMOVBU, AMOVHU, AMOVWU, AMOVF, AMOVD:
- rewriteMOV(ctxt, newprog, p)
- }
- }
- // Split immediates larger than 12-bits.
- for p := cursym.Func.Text; p != nil; p = p.Link {
- switch p.As {
- // <opi> $imm, REG, TO
- case AADDI, AANDI, AORI, AXORI:
- // LUI $high, TMP
- // ADDI $low, TMP, TMP
- // <op> TMP, REG, TO
- q := *p
- low, high, err := Split32BitImmediate(p.From.Offset)
- if err != nil {
- ctxt.Diag("%v: constant %d too large", p, p.From.Offset, err)
- }
- if high == 0 {
- break // no need to split
- }
- p.As = ALUI
- p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: high}
- p.Reg = 0
- p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
- p.Spadj = 0 // needed if TO is SP
- p = obj.Appendp(p, newprog)
- p.As = AADDIW
- p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: low}
- p.Reg = REG_TMP
- p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
- p = obj.Appendp(p, newprog)
- switch q.As {
- case AADDI:
- p.As = AADD
- case AANDI:
- p.As = AAND
- case AORI:
- p.As = AOR
- case AXORI:
- p.As = AXOR
- default:
- ctxt.Diag("unsupported instruction %v for splitting", q)
- }
- p.Spadj = q.Spadj
- p.To = q.To
- p.Reg = q.Reg
- p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
- // <load> $imm, REG, TO (load $imm+(REG), TO)
- case ALD, ALB, ALH, ALW, ALBU, ALHU, ALWU, AFLW, AFLD:
- low, high, err := Split32BitImmediate(p.From.Offset)
- if err != nil {
- ctxt.Diag("%v: constant %d too large", p, p.From.Offset)
- }
- if high == 0 {
- break // no need to split
- }
- q := *p
- // LUI $high, TMP
- // ADD TMP, REG, TMP
- // <load> $low, TMP, TO
- p.As = ALUI
- p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: high}
- p.Reg = 0
- p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
- p.Spadj = 0 // needed if TO is SP
- p = obj.Appendp(p, newprog)
- p.As = AADD
- p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
- p.Reg = q.From.Reg
- p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
- p = obj.Appendp(p, newprog)
- p.As = q.As
- p.To = q.To
- p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_TMP, Offset: low}
- p.Reg = obj.REG_NONE
- // <store> $imm, REG, TO (store $imm+(TO), REG)
- case ASD, ASB, ASH, ASW, AFSW, AFSD:
- low, high, err := Split32BitImmediate(p.To.Offset)
- if err != nil {
- ctxt.Diag("%v: constant %d too large", p, p.To.Offset)
- }
- if high == 0 {
- break // no need to split
- }
- q := *p
- // LUI $high, TMP
- // ADD TMP, TO, TMP
- // <store> $low, REG, TMP
- p.As = ALUI
- p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: high}
- p.Reg = 0
- p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
- p.Spadj = 0 // needed if TO is SP
- p = obj.Appendp(p, newprog)
- p.As = AADD
- p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
- p.Reg = q.To.Reg
- p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
- p = obj.Appendp(p, newprog)
- p.As = q.As
- p.From = obj.Addr{Type: obj.TYPE_REG, Reg: q.From.Reg, Offset: 0}
- p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_TMP, Offset: low}
- }
- }
- // Compute instruction addresses. Once we do that, we need to check for
- // overextended jumps and branches. Within each iteration, Pc differences
- // are always lower bounds (since the program gets monotonically longer,
- // a fixed point will be reached). No attempt to handle functions > 2GiB.
- for {
- rescan := false
- setPCs(cursym.Func.Text, 0)
- for p := cursym.Func.Text; p != nil; p = p.Link {
- switch p.As {
- case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ:
- if p.To.Type != obj.TYPE_BRANCH {
- panic("assemble: instruction with branch-like opcode lacks destination")
- }
- offset := p.To.Target().Pc - p.Pc
- if offset < -4096 || 4096 <= offset {
- // Branch is long. Replace it with a jump.
- jmp := obj.Appendp(p, newprog)
- jmp.As = AJAL
- jmp.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO}
- jmp.To = obj.Addr{Type: obj.TYPE_BRANCH}
- jmp.To.SetTarget(p.To.Target())
- p.As = InvertBranch(p.As)
- p.To.SetTarget(jmp.Link)
- // We may have made previous branches too long,
- // so recheck them.
- rescan = true
- }
- case AJAL:
- if p.To.Target() == nil {
- panic("intersymbol jumps should be expressed as AUIPC+JALR")
- }
- offset := p.To.Target().Pc - p.Pc
- if offset < -(1<<20) || (1<<20) <= offset {
- // Replace with 2-instruction sequence. This assumes
- // that TMP is not live across J instructions, since
- // it is reserved by SSA.
- jmp := obj.Appendp(p, newprog)
- jmp.As = AJALR
- jmp.From = p.From
- jmp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
- // p.From is not generally valid, however will be
- // fixed up in the next loop.
- p.As = AAUIPC
- p.From = obj.Addr{Type: obj.TYPE_BRANCH, Sym: p.From.Sym}
- p.From.SetTarget(p.To.Target())
- p.Reg = 0
- p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
- rescan = true
- }
- }
- }
- if !rescan {
- break
- }
- }
- // Now that there are no long branches, resolve branch and jump targets.
- // At this point, instruction rewriting which changes the number of
- // instructions will break everything--don't do it!
- for p := cursym.Func.Text; p != nil; p = p.Link {
- switch p.As {
- case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ, AJAL:
- switch p.To.Type {
- case obj.TYPE_BRANCH:
- p.To.Type, p.To.Offset = obj.TYPE_CONST, p.To.Target().Pc-p.Pc
- case obj.TYPE_MEM:
- panic("unhandled type")
- }
- case AAUIPC:
- if p.From.Type == obj.TYPE_BRANCH {
- low, high, err := Split32BitImmediate(p.From.Target().Pc - p.Pc)
- if err != nil {
- ctxt.Diag("%v: jump displacement %d too large", p, p.To.Target().Pc-p.Pc)
- }
- p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: high, Sym: cursym}
- p.Link.From.Offset = low
- }
- }
- }
- // Validate all instructions - this provides nice error messages.
- for p := cursym.Func.Text; p != nil; p = p.Link {
- for _, ins := range instructionsForProg(p) {
- ins.validate(ctxt)
- }
- }
- }
- func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgAlloc, framesize int64) *obj.Prog {
- // Leaf function with no frame is effectively NOSPLIT.
- if framesize == 0 {
- return p
- }
- // MOV g_stackguard(g), X10
- p = obj.Appendp(p, newprog)
- p.As = AMOV
- p.From.Type = obj.TYPE_MEM
- p.From.Reg = REGG
- p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
- if cursym.CFunc() {
- p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
- }
- p.To.Type = obj.TYPE_REG
- p.To.Reg = REG_X10
- var to_done, to_more *obj.Prog
- if framesize <= objabi.StackSmall {
- // small stack: SP < stackguard
- // BLTU SP, stackguard, done
- p = obj.Appendp(p, newprog)
- p.As = ABLTU
- p.From.Type = obj.TYPE_REG
- p.From.Reg = REG_X10
- p.Reg = REG_SP
- p.To.Type = obj.TYPE_BRANCH
- to_done = p
- } else if framesize <= objabi.StackBig {
- // large stack: SP-framesize < stackguard-StackSmall
- // ADD $-(framesize-StackSmall), SP, X11
- // BLTU X11, stackguard, done
- p = obj.Appendp(p, newprog)
- // TODO(sorear): logic inconsistent with comment, but both match all non-x86 arches
- p.As = AADDI
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = -(int64(framesize) - objabi.StackSmall)
- p.Reg = REG_SP
- p.To.Type = obj.TYPE_REG
- p.To.Reg = REG_X11
- p = obj.Appendp(p, newprog)
- p.As = ABLTU
- p.From.Type = obj.TYPE_REG
- p.From.Reg = REG_X10
- p.Reg = REG_X11
- p.To.Type = obj.TYPE_BRANCH
- to_done = p
- } else {
- // Such a large stack we need to protect against wraparound.
- // If SP is close to zero:
- // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
- // The +StackGuard on both sides is required to keep the left side positive:
- // SP is allowed to be slightly below stackguard. See stack.h.
- //
- // Preemption sets stackguard to StackPreempt, a very large value.
- // That breaks the math above, so we have to check for that explicitly.
- // // stackguard is X10
- // MOV $StackPreempt, X11
- // BEQ X10, X11, more
- // ADD $StackGuard, SP, X11
- // SUB X10, X11
- // MOV $(framesize+(StackGuard-StackSmall)), X10
- // BGTU X11, X10, done
- p = obj.Appendp(p, newprog)
- p.As = AMOV
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = objabi.StackPreempt
- p.To.Type = obj.TYPE_REG
- p.To.Reg = REG_X11
- p = obj.Appendp(p, newprog)
- to_more = p
- p.As = ABEQ
- p.From.Type = obj.TYPE_REG
- p.From.Reg = REG_X10
- p.Reg = REG_X11
- p.To.Type = obj.TYPE_BRANCH
- p = obj.Appendp(p, newprog)
- p.As = AADDI
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = int64(objabi.StackGuard)
- p.Reg = REG_SP
- p.To.Type = obj.TYPE_REG
- p.To.Reg = REG_X11
- p = obj.Appendp(p, newprog)
- p.As = ASUB
- p.From.Type = obj.TYPE_REG
- p.From.Reg = REG_X10
- p.Reg = REG_X11
- p.To.Type = obj.TYPE_REG
- p.To.Reg = REG_X11
- p = obj.Appendp(p, newprog)
- p.As = AMOV
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall
- p.To.Type = obj.TYPE_REG
- p.To.Reg = REG_X10
- p = obj.Appendp(p, newprog)
- p.As = ABLTU
- p.From.Type = obj.TYPE_REG
- p.From.Reg = REG_X10
- p.Reg = REG_X11
- p.To.Type = obj.TYPE_BRANCH
- to_done = p
- }
- p = ctxt.EmitEntryLiveness(cursym, p, newprog)
- // CALL runtime.morestack(SB)
- p = obj.Appendp(p, newprog)
- p.As = obj.ACALL
- p.To.Type = obj.TYPE_BRANCH
- if cursym.CFunc() {
- p.To.Sym = ctxt.Lookup("runtime.morestackc")
- } else if !cursym.Func.Text.From.Sym.NeedCtxt() {
- p.To.Sym = ctxt.Lookup("runtime.morestack_noctxt")
- } else {
- p.To.Sym = ctxt.Lookup("runtime.morestack")
- }
- if to_more != nil {
- to_more.To.SetTarget(p)
- }
- p = jalrToSym(ctxt, p, newprog, REG_X5)
- // JMP start
- p = obj.Appendp(p, newprog)
- p.As = AJAL
- p.To = obj.Addr{Type: obj.TYPE_BRANCH}
- p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO}
- p.To.SetTarget(cursym.Func.Text.Link)
- // placeholder for to_done's jump target
- p = obj.Appendp(p, newprog)
- p.As = obj.ANOP // zero-width place holder
- to_done.To.SetTarget(p)
- return p
- }
- // signExtend sign extends val starting at bit bit.
- func signExtend(val int64, bit uint) int64 {
- return val << (64 - bit) >> (64 - bit)
- }
- // Split32BitImmediate splits a signed 32-bit immediate into a signed 20-bit
- // upper immediate and a signed 12-bit lower immediate to be added to the upper
- // result. For example, high may be used in LUI and low in a following ADDI to
- // generate a full 32-bit constant.
- func Split32BitImmediate(imm int64) (low, high int64, err error) {
- if !immIFits(imm, 32) {
- return 0, 0, fmt.Errorf("immediate does not fit in 32-bits: %d", imm)
- }
- // Nothing special needs to be done if the immediate fits in 12-bits.
- if immIFits(imm, 12) {
- return imm, 0, nil
- }
- high = imm >> 12
- // The bottom 12 bits will be treated as signed.
- //
- // If that will result in a negative 12 bit number, add 1 to
- // our upper bits to adjust for the borrow.
- //
- // It is not possible for this increment to overflow. To
- // overflow, the 20 top bits would be 1, and the sign bit for
- // the low 12 bits would be set, in which case the entire 32
- // bit pattern fits in a 12 bit signed value.
- if imm&(1<<11) != 0 {
- high++
- }
- low = signExtend(imm, 12)
- high = signExtend(high, 20)
- return low, high, nil
- }
- func regVal(r, min, max uint32) uint32 {
- if r < min || r > max {
- panic(fmt.Sprintf("register out of range, want %d < %d < %d", min, r, max))
- }
- return r - min
- }
- // regI returns an integer register.
- func regI(r uint32) uint32 {
- return regVal(r, REG_X0, REG_X31)
- }
- // regF returns a float register.
- func regF(r uint32) uint32 {
- return regVal(r, REG_F0, REG_F31)
- }
- // regAddr extracts a register from an Addr.
- func regAddr(a obj.Addr, min, max uint32) uint32 {
- if a.Type != obj.TYPE_REG {
- panic(fmt.Sprintf("ill typed: %+v", a))
- }
- return regVal(uint32(a.Reg), min, max)
- }
- // regIAddr extracts the integer register from an Addr.
- func regIAddr(a obj.Addr) uint32 {
- return regAddr(a, REG_X0, REG_X31)
- }
- // regFAddr extracts the float register from an Addr.
- func regFAddr(a obj.Addr) uint32 {
- return regAddr(a, REG_F0, REG_F31)
- }
- // immIFits reports whether immediate value x fits in nbits bits
- // as a signed integer.
- func immIFits(x int64, nbits uint) bool {
- nbits--
- var min int64 = -1 << nbits
- var max int64 = 1<<nbits - 1
- return min <= x && x <= max
- }
- // immI extracts the signed integer of the specified size from an immediate.
- func immI(as obj.As, imm int64, nbits uint) uint32 {
- if !immIFits(imm, nbits) {
- panic(fmt.Sprintf("%v\tsigned immediate %d cannot fit in %d bits", as, imm, nbits))
- }
- return uint32(imm)
- }
- func wantImmI(ctxt *obj.Link, as obj.As, imm int64, nbits uint) {
- if !immIFits(imm, nbits) {
- ctxt.Diag("%v\tsigned immediate cannot be larger than %d bits but got %d", as, nbits, imm)
- }
- }
- func wantReg(ctxt *obj.Link, as obj.As, pos string, descr string, r, min, max uint32) {
- if r < min || r > max {
- var suffix string
- if r != obj.REG_NONE {
- suffix = fmt.Sprintf(" but got non-%s register %s", descr, RegName(int(r)))
- }
- ctxt.Diag("%v\texpected %s register in %s position%s", as, descr, pos, suffix)
- }
- }
- func wantNoneReg(ctxt *obj.Link, as obj.As, pos string, r uint32) {
- if r != obj.REG_NONE {
- ctxt.Diag("%v\texpected no register in %s but got register %s", as, pos, RegName(int(r)))
- }
- }
- // wantIntReg checks that r is an integer register.
- func wantIntReg(ctxt *obj.Link, as obj.As, pos string, r uint32) {
- wantReg(ctxt, as, pos, "integer", r, REG_X0, REG_X31)
- }
- // wantFloatReg checks that r is a floating-point register.
- func wantFloatReg(ctxt *obj.Link, as obj.As, pos string, r uint32) {
- wantReg(ctxt, as, pos, "float", r, REG_F0, REG_F31)
- }
- // wantEvenOffset checks that the offset is a multiple of two.
- func wantEvenOffset(ctxt *obj.Link, as obj.As, offset int64) {
- if offset%1 != 0 {
- ctxt.Diag("%v\tjump offset %v must be even", as, offset)
- }
- }
- func validateRIII(ctxt *obj.Link, ins *instruction) {
- wantIntReg(ctxt, ins.as, "rd", ins.rd)
- wantIntReg(ctxt, ins.as, "rs1", ins.rs1)
- wantIntReg(ctxt, ins.as, "rs2", ins.rs2)
- }
- func validateRFFF(ctxt *obj.Link, ins *instruction) {
- wantFloatReg(ctxt, ins.as, "rd", ins.rd)
- wantFloatReg(ctxt, ins.as, "rs1", ins.rs1)
- wantFloatReg(ctxt, ins.as, "rs2", ins.rs2)
- }
- func validateRFFI(ctxt *obj.Link, ins *instruction) {
- wantIntReg(ctxt, ins.as, "rd", ins.rd)
- wantFloatReg(ctxt, ins.as, "rs1", ins.rs1)
- wantFloatReg(ctxt, ins.as, "rs2", ins.rs2)
- }
- func validateRFI(ctxt *obj.Link, ins *instruction) {
- wantIntReg(ctxt, ins.as, "rd", ins.rd)
- wantNoneReg(ctxt, ins.as, "rs1", ins.rs1)
- wantFloatReg(ctxt, ins.as, "rs2", ins.rs2)
- }
- func validateRIF(ctxt *obj.Link, ins *instruction) {
- wantFloatReg(ctxt, ins.as, "rd", ins.rd)
- wantNoneReg(ctxt, ins.as, "rs1", ins.rs1)
- wantIntReg(ctxt, ins.as, "rs2", ins.rs2)
- }
- func validateRFF(ctxt *obj.Link, ins *instruction) {
- wantFloatReg(ctxt, ins.as, "rd", ins.rd)
- wantNoneReg(ctxt, ins.as, "rs1", ins.rs1)
- wantFloatReg(ctxt, ins.as, "rs2", ins.rs2)
- }
- func validateII(ctxt *obj.Link, ins *instruction) {
- wantImmI(ctxt, ins.as, ins.imm, 12)
- wantIntReg(ctxt, ins.as, "rd", ins.rd)
- wantIntReg(ctxt, ins.as, "rs1", ins.rs1)
- }
- func validateIF(ctxt *obj.Link, ins *instruction) {
- wantImmI(ctxt, ins.as, ins.imm, 12)
- wantFloatReg(ctxt, ins.as, "rd", ins.rd)
- wantIntReg(ctxt, ins.as, "rs1", ins.rs1)
- }
- func validateSI(ctxt *obj.Link, ins *instruction) {
- wantImmI(ctxt, ins.as, ins.imm, 12)
- wantIntReg(ctxt, ins.as, "rd", ins.rd)
- wantIntReg(ctxt, ins.as, "rs1", ins.rs1)
- }
- func validateSF(ctxt *obj.Link, ins *instruction) {
- wantImmI(ctxt, ins.as, ins.imm, 12)
- wantIntReg(ctxt, ins.as, "rd", ins.rd)
- wantFloatReg(ctxt, ins.as, "rs1", ins.rs1)
- }
- func validateB(ctxt *obj.Link, ins *instruction) {
- // Offsets are multiples of two, so accept 13 bit immediates for the
- // 12 bit slot. We implicitly drop the least significant bit in encodeB.
- wantEvenOffset(ctxt, ins.as, ins.imm)
- wantImmI(ctxt, ins.as, ins.imm, 13)
- wantNoneReg(ctxt, ins.as, "rd", ins.rd)
- wantIntReg(ctxt, ins.as, "rs1", ins.rs1)
- wantIntReg(ctxt, ins.as, "rs2", ins.rs2)
- }
- func validateU(ctxt *obj.Link, ins *instruction) {
- wantImmI(ctxt, ins.as, ins.imm, 20)
- wantIntReg(ctxt, ins.as, "rd", ins.rd)
- wantNoneReg(ctxt, ins.as, "rs1", ins.rs1)
- wantNoneReg(ctxt, ins.as, "rs2", ins.rs2)
- }
- func validateJ(ctxt *obj.Link, ins *instruction) {
- // Offsets are multiples of two, so accept 21 bit immediates for the
- // 20 bit slot. We implicitly drop the least significant bit in encodeJ.
- wantEvenOffset(ctxt, ins.as, ins.imm)
- wantImmI(ctxt, ins.as, ins.imm, 21)
- wantIntReg(ctxt, ins.as, "rd", ins.rd)
- wantNoneReg(ctxt, ins.as, "rs1", ins.rs1)
- wantNoneReg(ctxt, ins.as, "rs2", ins.rs2)
- }
- func validateRaw(ctxt *obj.Link, ins *instruction) {
- // Treat the raw value specially as a 32-bit unsigned integer.
- // Nobody wants to enter negative machine code.
- if ins.imm < 0 || 1<<32 <= ins.imm {
- ctxt.Diag("%v\timmediate in raw position cannot be larger than 32 bits but got %d", ins.as, ins.imm)
- }
- }
- // encodeR encodes an R-type RISC-V instruction.
- func encodeR(as obj.As, rs1, rs2, rd, funct3, funct7 uint32) uint32 {
- enc := encode(as)
- if enc == nil {
- panic("encodeR: could not encode instruction")
- }
- if enc.rs2 != 0 && rs2 != 0 {
- panic("encodeR: instruction uses rs2, but rs2 was nonzero")
- }
- return funct7<<25 | enc.funct7<<25 | enc.rs2<<20 | rs2<<20 | rs1<<15 | enc.funct3<<12 | funct3<<12 | rd<<7 | enc.opcode
- }
- func encodeRIII(ins *instruction) uint32 {
- return encodeR(ins.as, regI(ins.rs1), regI(ins.rs2), regI(ins.rd), ins.funct3, ins.funct7)
- }
- func encodeRFFF(ins *instruction) uint32 {
- return encodeR(ins.as, regF(ins.rs1), regF(ins.rs2), regF(ins.rd), ins.funct3, ins.funct7)
- }
- func encodeRFFI(ins *instruction) uint32 {
- return encodeR(ins.as, regF(ins.rs1), regF(ins.rs2), regI(ins.rd), ins.funct3, ins.funct7)
- }
- func encodeRFI(ins *instruction) uint32 {
- return encodeR(ins.as, regF(ins.rs2), 0, regI(ins.rd), ins.funct3, ins.funct7)
- }
- func encodeRIF(ins *instruction) uint32 {
- return encodeR(ins.as, regI(ins.rs2), 0, regF(ins.rd), ins.funct3, ins.funct7)
- }
- func encodeRFF(ins *instruction) uint32 {
- return encodeR(ins.as, regF(ins.rs2), 0, regF(ins.rd), ins.funct3, ins.funct7)
- }
- // encodeI encodes an I-type RISC-V instruction.
- func encodeI(as obj.As, rs1, rd, imm uint32) uint32 {
- enc := encode(as)
- if enc == nil {
- panic("encodeI: could not encode instruction")
- }
- imm |= uint32(enc.csr)
- return imm<<20 | rs1<<15 | enc.funct3<<12 | rd<<7 | enc.opcode
- }
- func encodeII(ins *instruction) uint32 {
- return encodeI(ins.as, regI(ins.rs1), regI(ins.rd), uint32(ins.imm))
- }
- func encodeIF(ins *instruction) uint32 {
- return encodeI(ins.as, regI(ins.rs1), regF(ins.rd), uint32(ins.imm))
- }
- // encodeS encodes an S-type RISC-V instruction.
- func encodeS(as obj.As, rs1, rs2, imm uint32) uint32 {
- enc := encode(as)
- if enc == nil {
- panic("encodeS: could not encode instruction")
- }
- return (imm>>5)<<25 | rs2<<20 | rs1<<15 | enc.funct3<<12 | (imm&0x1f)<<7 | enc.opcode
- }
- func encodeSI(ins *instruction) uint32 {
- return encodeS(ins.as, regI(ins.rd), regI(ins.rs1), uint32(ins.imm))
- }
- func encodeSF(ins *instruction) uint32 {
- return encodeS(ins.as, regI(ins.rd), regF(ins.rs1), uint32(ins.imm))
- }
- // encodeB encodes a B-type RISC-V instruction.
- func encodeB(ins *instruction) uint32 {
- imm := immI(ins.as, ins.imm, 13)
- rs2 := regI(ins.rs1)
- rs1 := regI(ins.rs2)
- enc := encode(ins.as)
- if enc == nil {
- panic("encodeB: could not encode instruction")
- }
- return (imm>>12)<<31 | ((imm>>5)&0x3f)<<25 | rs2<<20 | rs1<<15 | enc.funct3<<12 | ((imm>>1)&0xf)<<8 | ((imm>>11)&0x1)<<7 | enc.opcode
- }
- // encodeU encodes a U-type RISC-V instruction.
- func encodeU(ins *instruction) uint32 {
- // The immediates for encodeU are the upper 20 bits of a 32 bit value.
- // Rather than have the user/compiler generate a 32 bit constant, the
- // bottommost bits of which must all be zero, instead accept just the
- // top bits.
- imm := immI(ins.as, ins.imm, 20)
- rd := regI(ins.rd)
- enc := encode(ins.as)
- if enc == nil {
- panic("encodeU: could not encode instruction")
- }
- return imm<<12 | rd<<7 | enc.opcode
- }
- // encodeJ encodes a J-type RISC-V instruction.
- func encodeJ(ins *instruction) uint32 {
- imm := immI(ins.as, ins.imm, 21)
- rd := regI(ins.rd)
- enc := encode(ins.as)
- if enc == nil {
- panic("encodeJ: could not encode instruction")
- }
- return (imm>>20)<<31 | ((imm>>1)&0x3ff)<<21 | ((imm>>11)&0x1)<<20 | ((imm>>12)&0xff)<<12 | rd<<7 | enc.opcode
- }
- func encodeRawIns(ins *instruction) uint32 {
- // Treat the raw value specially as a 32-bit unsigned integer.
- // Nobody wants to enter negative machine code.
- if ins.imm < 0 || 1<<32 <= ins.imm {
- panic(fmt.Sprintf("immediate %d cannot fit in 32 bits", ins.imm))
- }
- return uint32(ins.imm)
- }
- func EncodeIImmediate(imm int64) (int64, error) {
- if !immIFits(imm, 12) {
- return 0, fmt.Errorf("immediate %#x does not fit in 12 bits", imm)
- }
- return imm << 20, nil
- }
- func EncodeSImmediate(imm int64) (int64, error) {
- if !immIFits(imm, 12) {
- return 0, fmt.Errorf("immediate %#x does not fit in 12 bits", imm)
- }
- return ((imm >> 5) << 25) | ((imm & 0x1f) << 7), nil
- }
- func EncodeUImmediate(imm int64) (int64, error) {
- if !immIFits(imm, 20) {
- return 0, fmt.Errorf("immediate %#x does not fit in 20 bits", imm)
- }
- return imm << 12, nil
- }
- type encoding struct {
- encode func(*instruction) uint32 // encode returns the machine code for an instruction
- validate func(*obj.Link, *instruction) // validate validates an instruction
- length int // length of encoded instruction; 0 for pseudo-ops, 4 otherwise
- }
- var (
- // Encodings have the following naming convention:
- //
- // 1. the instruction encoding (R/I/S/B/U/J), in lowercase
- // 2. zero or more register operand identifiers (I = integer
- // register, F = float register), in uppercase
- // 3. the word "Encoding"
- //
- // For example, rIIIEncoding indicates an R-type instruction with two
- // integer register inputs and an integer register output; sFEncoding
- // indicates an S-type instruction with rs2 being a float register.
- rIIIEncoding = encoding{encode: encodeRIII, validate: validateRIII, length: 4}
- rFFFEncoding = encoding{encode: encodeRFFF, validate: validateRFFF, length: 4}
- rFFIEncoding = encoding{encode: encodeRFFI, validate: validateRFFI, length: 4}
- rFIEncoding = encoding{encode: encodeRFI, validate: validateRFI, length: 4}
- rIFEncoding = encoding{encode: encodeRIF, validate: validateRIF, length: 4}
- rFFEncoding = encoding{encode: encodeRFF, validate: validateRFF, length: 4}
- iIEncoding = encoding{encode: encodeII, validate: validateII, length: 4}
- iFEncoding = encoding{encode: encodeIF, validate: validateIF, length: 4}
- sIEncoding = encoding{encode: encodeSI, validate: validateSI, length: 4}
- sFEncoding = encoding{encode: encodeSF, validate: validateSF, length: 4}
- bEncoding = encoding{encode: encodeB, validate: validateB, length: 4}
- uEncoding = encoding{encode: encodeU, validate: validateU, length: 4}
- jEncoding = encoding{encode: encodeJ, validate: validateJ, length: 4}
- // rawEncoding encodes a raw instruction byte sequence.
- rawEncoding = encoding{encode: encodeRawIns, validate: validateRaw, length: 4}
- // pseudoOpEncoding panics if encoding is attempted, but does no validation.
- pseudoOpEncoding = encoding{encode: nil, validate: func(*obj.Link, *instruction) {}, length: 0}
- // badEncoding is used when an invalid op is encountered.
- // An error has already been generated, so let anything else through.
- badEncoding = encoding{encode: func(*instruction) uint32 { return 0 }, validate: func(*obj.Link, *instruction) {}, length: 0}
- )
- // encodings contains the encodings for RISC-V instructions.
- // Instructions are masked with obj.AMask to keep indices small.
- var encodings = [ALAST & obj.AMask]encoding{
- // Unprivileged ISA
- // 2.4: Integer Computational Instructions
- AADDI & obj.AMask: iIEncoding,
- ASLTI & obj.AMask: iIEncoding,
- ASLTIU & obj.AMask: iIEncoding,
- AANDI & obj.AMask: iIEncoding,
- AORI & obj.AMask: iIEncoding,
- AXORI & obj.AMask: iIEncoding,
- ASLLI & obj.AMask: iIEncoding,
- ASRLI & obj.AMask: iIEncoding,
- ASRAI & obj.AMask: iIEncoding,
- ALUI & obj.AMask: uEncoding,
- AAUIPC & obj.AMask: uEncoding,
- AADD & obj.AMask: rIIIEncoding,
- ASLT & obj.AMask: rIIIEncoding,
- ASLTU & obj.AMask: rIIIEncoding,
- AAND & obj.AMask: rIIIEncoding,
- AOR & obj.AMask: rIIIEncoding,
- AXOR & obj.AMask: rIIIEncoding,
- ASLL & obj.AMask: rIIIEncoding,
- ASRL & obj.AMask: rIIIEncoding,
- ASUB & obj.AMask: rIIIEncoding,
- ASRA & obj.AMask: rIIIEncoding,
- // 2.5: Control Transfer Instructions
- AJAL & obj.AMask: jEncoding,
- AJALR & obj.AMask: iIEncoding,
- ABEQ & obj.AMask: bEncoding,
- ABNE & obj.AMask: bEncoding,
- ABLT & obj.AMask: bEncoding,
- ABLTU & obj.AMask: bEncoding,
- ABGE & obj.AMask: bEncoding,
- ABGEU & obj.AMask: bEncoding,
- // 2.6: Load and Store Instructions
- ALW & obj.AMask: iIEncoding,
- ALWU & obj.AMask: iIEncoding,
- ALH & obj.AMask: iIEncoding,
- ALHU & obj.AMask: iIEncoding,
- ALB & obj.AMask: iIEncoding,
- ALBU & obj.AMask: iIEncoding,
- ASW & obj.AMask: sIEncoding,
- ASH & obj.AMask: sIEncoding,
- ASB & obj.AMask: sIEncoding,
- // 2.7: Memory Ordering
- AFENCE & obj.AMask: iIEncoding,
- // 5.2: Integer Computational Instructions (RV64I)
- AADDIW & obj.AMask: iIEncoding,
- ASLLIW & obj.AMask: iIEncoding,
- ASRLIW & obj.AMask: iIEncoding,
- ASRAIW & obj.AMask: iIEncoding,
- AADDW & obj.AMask: rIIIEncoding,
- ASLLW & obj.AMask: rIIIEncoding,
- ASRLW & obj.AMask: rIIIEncoding,
- ASUBW & obj.AMask: rIIIEncoding,
- ASRAW & obj.AMask: rIIIEncoding,
- // 5.3: Load and Store Instructions (RV64I)
- ALD & obj.AMask: iIEncoding,
- ASD & obj.AMask: sIEncoding,
- // 7.1: Multiplication Operations
- AMUL & obj.AMask: rIIIEncoding,
- AMULH & obj.AMask: rIIIEncoding,
- AMULHU & obj.AMask: rIIIEncoding,
- AMULHSU & obj.AMask: rIIIEncoding,
- AMULW & obj.AMask: rIIIEncoding,
- ADIV & obj.AMask: rIIIEncoding,
- ADIVU & obj.AMask: rIIIEncoding,
- AREM & obj.AMask: rIIIEncoding,
- AREMU & obj.AMask: rIIIEncoding,
- ADIVW & obj.AMask: rIIIEncoding,
- ADIVUW & obj.AMask: rIIIEncoding,
- AREMW & obj.AMask: rIIIEncoding,
- AREMUW & obj.AMask: rIIIEncoding,
- // 8.2: Load-Reserved/Store-Conditional
- ALRW & obj.AMask: rIIIEncoding,
- ALRD & obj.AMask: rIIIEncoding,
- ASCW & obj.AMask: rIIIEncoding,
- ASCD & obj.AMask: rIIIEncoding,
- // 8.3: Atomic Memory Operations
- AAMOSWAPW & obj.AMask: rIIIEncoding,
- AAMOSWAPD & obj.AMask: rIIIEncoding,
- AAMOADDW & obj.AMask: rIIIEncoding,
- AAMOADDD & obj.AMask: rIIIEncoding,
- AAMOANDW & obj.AMask: rIIIEncoding,
- AAMOANDD & obj.AMask: rIIIEncoding,
- AAMOORW & obj.AMask: rIIIEncoding,
- AAMOORD & obj.AMask: rIIIEncoding,
- AAMOXORW & obj.AMask: rIIIEncoding,
- AAMOXORD & obj.AMask: rIIIEncoding,
- AAMOMAXW & obj.AMask: rIIIEncoding,
- AAMOMAXD & obj.AMask: rIIIEncoding,
- AAMOMAXUW & obj.AMask: rIIIEncoding,
- AAMOMAXUD & obj.AMask: rIIIEncoding,
- AAMOMINW & obj.AMask: rIIIEncoding,
- AAMOMIND & obj.AMask: rIIIEncoding,
- AAMOMINUW & obj.AMask: rIIIEncoding,
- AAMOMINUD & obj.AMask: rIIIEncoding,
- // 10.1: Base Counters and Timers
- ARDCYCLE & obj.AMask: iIEncoding,
- ARDTIME & obj.AMask: iIEncoding,
- ARDINSTRET & obj.AMask: iIEncoding,
- // 11.5: Single-Precision Load and Store Instructions
- AFLW & obj.AMask: iFEncoding,
- AFSW & obj.AMask: sFEncoding,
- // 11.6: Single-Precision Floating-Point Computational Instructions
- AFADDS & obj.AMask: rFFFEncoding,
- AFSUBS & obj.AMask: rFFFEncoding,
- AFMULS & obj.AMask: rFFFEncoding,
- AFDIVS & obj.AMask: rFFFEncoding,
- AFMINS & obj.AMask: rFFFEncoding,
- AFMAXS & obj.AMask: rFFFEncoding,
- AFSQRTS & obj.AMask: rFFFEncoding,
- // 11.7: Single-Precision Floating-Point Conversion and Move Instructions
- AFCVTWS & obj.AMask: rFIEncoding,
- AFCVTLS & obj.AMask: rFIEncoding,
- AFCVTSW & obj.AMask: rIFEncoding,
- AFCVTSL & obj.AMask: rIFEncoding,
- AFCVTWUS & obj.AMask: rFIEncoding,
- AFCVTLUS & obj.AMask: rFIEncoding,
- AFCVTSWU & obj.AMask: rIFEncoding,
- AFCVTSLU & obj.AMask: rIFEncoding,
- AFSGNJS & obj.AMask: rFFFEncoding,
- AFSGNJNS & obj.AMask: rFFFEncoding,
- AFSGNJXS & obj.AMask: rFFFEncoding,
- AFMVXS & obj.AMask: rFIEncoding,
- AFMVSX & obj.AMask: rIFEncoding,
- AFMVXW & obj.AMask: rFIEncoding,
- AFMVWX & obj.AMask: rIFEncoding,
- // 11.8: Single-Precision Floating-Point Compare Instructions
- AFEQS & obj.AMask: rFFIEncoding,
- AFLTS & obj.AMask: rFFIEncoding,
- AFLES & obj.AMask: rFFIEncoding,
- // 11.9: Single-Precision Floating-Point Classify Instruction
- AFCLASSS & obj.AMask: rFIEncoding,
- // 12.3: Double-Precision Load and Store Instructions
- AFLD & obj.AMask: iFEncoding,
- AFSD & obj.AMask: sFEncoding,
- // 12.4: Double-Precision Floating-Point Computational Instructions
- AFADDD & obj.AMask: rFFFEncoding,
- AFSUBD & obj.AMask: rFFFEncoding,
- AFMULD & obj.AMask: rFFFEncoding,
- AFDIVD & obj.AMask: rFFFEncoding,
- AFMIND & obj.AMask: rFFFEncoding,
- AFMAXD & obj.AMask: rFFFEncoding,
- AFSQRTD & obj.AMask: rFFFEncoding,
- // 12.5: Double-Precision Floating-Point Conversion and Move Instructions
- AFCVTWD & obj.AMask: rFIEncoding,
- AFCVTLD & obj.AMask: rFIEncoding,
- AFCVTDW & obj.AMask: rIFEncoding,
- AFCVTDL & obj.AMask: rIFEncoding,
- AFCVTWUD & obj.AMask: rFIEncoding,
- AFCVTLUD & obj.AMask: rFIEncoding,
- AFCVTDWU & obj.AMask: rIFEncoding,
- AFCVTDLU & obj.AMask: rIFEncoding,
- AFCVTSD & obj.AMask: rFFEncoding,
- AFCVTDS & obj.AMask: rFFEncoding,
- AFSGNJD & obj.AMask: rFFFEncoding,
- AFSGNJND & obj.AMask: rFFFEncoding,
- AFSGNJXD & obj.AMask: rFFFEncoding,
- AFMVXD & obj.AMask: rFIEncoding,
- AFMVDX & obj.AMask: rIFEncoding,
- // 12.6: Double-Precision Floating-Point Compare Instructions
- AFEQD & obj.AMask: rFFIEncoding,
- AFLTD & obj.AMask: rFFIEncoding,
- AFLED & obj.AMask: rFFIEncoding,
- // 12.7: Double-Precision Floating-Point Classify Instruction
- AFCLASSD & obj.AMask: rFIEncoding,
- // Privileged ISA
- // 3.2.1: Environment Call and Breakpoint
- AECALL & obj.AMask: iIEncoding,
- AEBREAK & obj.AMask: iIEncoding,
- // Escape hatch
- AWORD & obj.AMask: rawEncoding,
- // Pseudo-operations
- obj.AFUNCDATA: pseudoOpEncoding,
- obj.APCDATA: pseudoOpEncoding,
- obj.ATEXT: pseudoOpEncoding,
- obj.ANOP: pseudoOpEncoding,
- }
- // encodingForAs returns the encoding for an obj.As.
- func encodingForAs(as obj.As) (encoding, error) {
- if base := as &^ obj.AMask; base != obj.ABaseRISCV && base != 0 {
- return badEncoding, fmt.Errorf("encodingForAs: not a RISC-V instruction %s", as)
- }
- asi := as & obj.AMask
- if int(asi) >= len(encodings) {
- return badEncoding, fmt.Errorf("encodingForAs: bad RISC-V instruction %s", as)
- }
- enc := encodings[asi]
- if enc.validate == nil {
- return badEncoding, fmt.Errorf("encodingForAs: no encoding for instruction %s", as)
- }
- return enc, nil
- }
- type instruction struct {
- as obj.As // Assembler opcode
- rd uint32 // Destination register
- rs1 uint32 // Source register 1
- rs2 uint32 // Source register 2
- imm int64 // Immediate
- funct3 uint32 // Function 3
- funct7 uint32 // Function 7
- }
- func (ins *instruction) encode() (uint32, error) {
- enc, err := encodingForAs(ins.as)
- if err != nil {
- return 0, err
- }
- if enc.length > 0 {
- return enc.encode(ins), nil
- }
- return 0, fmt.Errorf("fixme")
- }
- func (ins *instruction) length() int {
- enc, err := encodingForAs(ins.as)
- if err != nil {
- return 0
- }
- return enc.length
- }
- func (ins *instruction) validate(ctxt *obj.Link) {
- enc, err := encodingForAs(ins.as)
- if err != nil {
- ctxt.Diag(err.Error())
- return
- }
- enc.validate(ctxt, ins)
- }
- // instructionsForProg returns the machine instructions for an *obj.Prog.
- func instructionsForProg(p *obj.Prog) []*instruction {
- ins := &instruction{
- as: p.As,
- rd: uint32(p.To.Reg),
- rs1: uint32(p.Reg),
- rs2: uint32(p.From.Reg),
- imm: p.From.Offset,
- }
- inss := []*instruction{ins}
- switch ins.as {
- case AJAL, AJALR:
- ins.rd, ins.rs1, ins.rs2 = uint32(p.From.Reg), uint32(p.To.Reg), obj.REG_NONE
- ins.imm = p.To.Offset
- case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ:
- switch ins.as {
- case ABEQZ:
- ins.as, ins.rs1, ins.rs2 = ABEQ, REG_ZERO, uint32(p.From.Reg)
- case ABGEZ:
- ins.as, ins.rs1, ins.rs2 = ABGE, REG_ZERO, uint32(p.From.Reg)
- case ABGT:
- ins.as, ins.rs1, ins.rs2 = ABLT, uint32(p.Reg), uint32(p.From.Reg)
- case ABGTU:
- ins.as, ins.rs1, ins.rs2 = ABLTU, uint32(p.Reg), uint32(p.From.Reg)
- case ABGTZ:
- ins.as, ins.rs1, ins.rs2 = ABLT, uint32(p.From.Reg), REG_ZERO
- case ABLE:
- ins.as, ins.rs1, ins.rs2 = ABGE, uint32(p.Reg), uint32(p.From.Reg)
- case ABLEU:
- ins.as, ins.rs1, ins.rs2 = ABGEU, uint32(p.Reg), uint32(p.From.Reg)
- case ABLEZ:
- ins.as, ins.rs1, ins.rs2 = ABGE, uint32(p.From.Reg), REG_ZERO
- case ABLTZ:
- ins.as, ins.rs1, ins.rs2 = ABLT, REG_ZERO, uint32(p.From.Reg)
- case ABNEZ:
- ins.as, ins.rs1, ins.rs2 = ABNE, REG_ZERO, uint32(p.From.Reg)
- }
- ins.imm = p.To.Offset
- case ALW, ALWU, ALH, ALHU, ALB, ALBU, ALD, AFLW, AFLD:
- if p.From.Type != obj.TYPE_MEM {
- p.Ctxt.Diag("%v requires memory for source", p)
- return nil
- }
- ins.rs1, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE
- ins.imm = p.From.Offset
- case ASW, ASH, ASB, ASD, AFSW, AFSD:
- if p.To.Type != obj.TYPE_MEM {
- p.Ctxt.Diag("%v requires memory for destination", p)
- return nil
- }
- ins.rs1, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE
- ins.imm = p.To.Offset
- case ALRW, ALRD:
- // Set aq to use acquire access ordering, which matches Go's memory requirements.
- ins.funct7 = 2
- ins.rs1, ins.rs2 = uint32(p.From.Reg), REG_ZERO
- case ASCW, ASCD, AAMOSWAPW, AAMOSWAPD, AAMOADDW, AAMOADDD, AAMOANDW, AAMOANDD, AAMOORW, AAMOORD,
- AAMOXORW, AAMOXORD, AAMOMINW, AAMOMIND, AAMOMINUW, AAMOMINUD, AAMOMAXW, AAMOMAXD, AAMOMAXUW, AAMOMAXUD:
- // Set aq to use acquire access ordering, which matches Go's memory requirements.
- ins.funct7 = 2
- ins.rd, ins.rs1, ins.rs2 = uint32(p.RegTo2), uint32(p.To.Reg), uint32(p.From.Reg)
- case AECALL, AEBREAK, ARDCYCLE, ARDTIME, ARDINSTRET:
- insEnc := encode(p.As)
- if p.To.Type == obj.TYPE_NONE {
- ins.rd = REG_ZERO
- }
- ins.rs1 = REG_ZERO
- ins.imm = insEnc.csr
- case AFENCE:
- ins.rd, ins.rs1, ins.rs2 = REG_ZERO, REG_ZERO, obj.REG_NONE
- ins.imm = 0x0ff
- case AFCVTWS, AFCVTLS, AFCVTWUS, AFCVTLUS, AFCVTWD, AFCVTLD, AFCVTWUD, AFCVTLUD:
- // Set the rounding mode in funct3 to round to zero.
- ins.funct3 = 1
- case AFNES, AFNED:
- // Replace FNE[SD] with FEQ[SD] and NOT.
- if p.To.Type != obj.TYPE_REG {
- p.Ctxt.Diag("%v needs an integer register output", ins.as)
- return nil
- }
- if ins.as == AFNES {
- ins.as = AFEQS
- } else {
- ins.as = AFEQD
- }
- ins = &instruction{
- as: AXORI, // [bit] xor 1 = not [bit]
- rd: ins.rd,
- rs1: ins.rd,
- imm: 1,
- }
- inss = append(inss, ins)
- case AFSQRTS, AFSQRTD:
- // These instructions expect a zero (i.e. float register 0)
- // to be the second input operand.
- ins.rs1 = uint32(p.From.Reg)
- ins.rs2 = REG_F0
- case ANEG, ANEGW:
- // NEG rs, rd -> SUB rs, X0, rd
- ins.as = ASUB
- if p.As == ANEGW {
- ins.as = ASUBW
- }
- ins.rs1 = REG_ZERO
- if ins.rd == obj.REG_NONE {
- ins.rd = ins.rs2
- }
- case ANOT:
- // NOT rs, rd -> XORI $-1, rs, rd
- ins.as = AXORI
- ins.rs1, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE
- if ins.rd == obj.REG_NONE {
- ins.rd = ins.rs1
- }
- ins.imm = -1
- case ASEQZ:
- // SEQZ rs, rd -> SLTIU $1, rs, rd
- ins.as = ASLTIU
- ins.rs1 = uint32(p.From.Reg)
- ins.imm = 1
- case ASNEZ:
- // SNEZ rs, rd -> SLTU rs, x0, rd
- ins.as = ASLTU
- ins.rs1 = REG_ZERO
- case AFNEGS:
- // FNEGS rs, rd -> FSGNJNS rs, rs, rd
- ins.as = AFSGNJNS
- ins.rs1 = uint32(p.From.Reg)
- case AFNEGD:
- // FNEGD rs, rd -> FSGNJND rs, rs, rd
- ins.as = AFSGNJND
- ins.rs1 = uint32(p.From.Reg)
- }
- return inss
- }
- // assemble emits machine code.
- // It is called at the very end of the assembly process.
- func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
- if ctxt.Retpoline {
- ctxt.Diag("-spectre=ret not supported on riscv")
- ctxt.Retpoline = false // don't keep printing
- }
- var symcode []uint32
- for p := cursym.Func.Text; p != nil; p = p.Link {
- switch p.As {
- case AJALR:
- if p.To.Sym != nil {
- // This is a CALL/JMP. We add a relocation only
- // for linker stack checking. No actual
- // relocation is needed.
- rel := obj.Addrel(cursym)
- rel.Off = int32(p.Pc)
- rel.Siz = 4
- rel.Sym = p.To.Sym
- rel.Add = p.To.Offset
- rel.Type = objabi.R_CALLRISCV
- }
- case AAUIPC:
- var rt objabi.RelocType
- if p.Mark&NEED_PCREL_ITYPE_RELOC == NEED_PCREL_ITYPE_RELOC {
- rt = objabi.R_RISCV_PCREL_ITYPE
- } else if p.Mark&NEED_PCREL_STYPE_RELOC == NEED_PCREL_STYPE_RELOC {
- rt = objabi.R_RISCV_PCREL_STYPE
- } else {
- break
- }
- if p.Link == nil {
- ctxt.Diag("AUIPC needing PC-relative reloc missing following instruction")
- break
- }
- addr := p.RestArgs[0]
- if addr.Sym == nil {
- ctxt.Diag("AUIPC needing PC-relative reloc missing symbol")
- break
- }
- rel := obj.Addrel(cursym)
- rel.Off = int32(p.Pc)
- rel.Siz = 8
- rel.Sym = addr.Sym
- rel.Add = addr.Offset
- rel.Type = rt
- }
- for _, ins := range instructionsForProg(p) {
- ic, err := ins.encode()
- if err == nil {
- symcode = append(symcode, ic)
- }
- }
- }
- cursym.Size = int64(4 * len(symcode))
- cursym.Grow(cursym.Size)
- for p, i := cursym.P, 0; i < len(symcode); p, i = p[4:], i+1 {
- ctxt.Arch.ByteOrder.PutUint32(p, symcode[i])
- }
- obj.MarkUnsafePoints(ctxt, cursym.Func.Text, newprog, isUnsafePoint, nil)
- }
- func isUnsafePoint(p *obj.Prog) bool {
- return p.From.Reg == REG_TMP || p.To.Reg == REG_TMP || p.Reg == REG_TMP
- }
- var LinkRISCV64 = obj.LinkArch{
- Arch: sys.ArchRISCV64,
- Init: buildop,
- Preprocess: preprocess,
- Assemble: assemble,
- Progedit: progedit,
- UnaryDst: unaryDst,
- DWARFRegisters: RISCV64DWARFRegisters,
- }
|