diff options
Diffstat (limited to 'vendor/github.com/twitchyliquid64/golang-asm/obj/riscv')
5 files changed, 0 insertions, 3393 deletions
diff --git a/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/anames.go b/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/anames.go deleted file mode 100644 index 230a01fe0..000000000 --- a/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/anames.go +++ /dev/null @@ -1,258 +0,0 @@ -// Code generated by stringer -i cpu.go -o anames.go -p riscv; DO NOT EDIT. - -package riscv - -import "github.com/twitchyliquid64/golang-asm/obj" - -var Anames = []string{ - obj.A_ARCHSPECIFIC: "ADDI", - "SLTI", - "SLTIU", - "ANDI", - "ORI", - "XORI", - "SLLI", - "SRLI", - "SRAI", - "LUI", - "AUIPC", - "ADD", - "SLT", - "SLTU", - "AND", - "OR", - "XOR", - "SLL", - "SRL", - "SUB", - "SRA", - "SLLIRV32", - "SRLIRV32", - "SRAIRV32", - "JAL", - "JALR", - "BEQ", - "BNE", - "BLT", - "BLTU", - "BGE", - "BGEU", - "LW", - "LWU", - "LH", - "LHU", - "LB", - "LBU", - "SW", - "SH", - "SB", - "FENCE", - "FENCEI", - "FENCETSO", - "ADDIW", - "SLLIW", - "SRLIW", - "SRAIW", - "ADDW", - "SLLW", - "SRLW", - "SUBW", - "SRAW", - "LD", - "SD", - "MUL", - "MULH", - "MULHU", - "MULHSU", - "MULW", - "DIV", - "DIVU", - "REM", - "REMU", - "DIVW", - "DIVUW", - "REMW", - "REMUW", - "LRD", - "SCD", - "LRW", - "SCW", - "AMOSWAPD", - "AMOADDD", - "AMOANDD", - "AMOORD", - "AMOXORD", - "AMOMAXD", - "AMOMAXUD", - "AMOMIND", - "AMOMINUD", - "AMOSWAPW", - "AMOADDW", - "AMOANDW", - "AMOORW", - "AMOXORW", - "AMOMAXW", - "AMOMAXUW", - "AMOMINW", - "AMOMINUW", - "RDCYCLE", - "RDCYCLEH", - "RDTIME", - "RDTIMEH", - "RDINSTRET", - "RDINSTRETH", - "FRCSR", - "FSCSR", - "FRRM", - "FSRM", - "FRFLAGS", - "FSFLAGS", - "FSRMI", - "FSFLAGSI", - "FLW", - "FSW", - "FADDS", - "FSUBS", - "FMULS", - "FDIVS", - "FMINS", - "FMAXS", - "FSQRTS", - "FMADDS", - "FMSUBS", - "FNMADDS", - "FNMSUBS", - "FCVTWS", - "FCVTLS", - "FCVTSW", - "FCVTSL", - "FCVTWUS", - "FCVTLUS", - "FCVTSWU", - "FCVTSLU", - "FSGNJS", - "FSGNJNS", - "FSGNJXS", - "FMVXS", - "FMVSX", - "FMVXW", - "FMVWX", - "FEQS", - "FLTS", - "FLES", - "FCLASSS", - "FLD", - "FSD", - "FADDD", - "FSUBD", - "FMULD", - "FDIVD", - "FMIND", - "FMAXD", - "FSQRTD", - "FMADDD", - "FMSUBD", - "FNMADDD", - "FNMSUBD", - "FCVTWD", - "FCVTLD", - "FCVTDW", - "FCVTDL", - "FCVTWUD", - "FCVTLUD", - "FCVTDWU", - "FCVTDLU", - "FCVTSD", - "FCVTDS", - "FSGNJD", - "FSGNJND", - "FSGNJXD", - "FMVXD", - "FMVDX", - "FEQD", - "FLTD", - "FLED", - "FCLASSD", - "FLQ", - "FSQ", - "FADDQ", - "FSUBQ", - "FMULQ", - "FDIVQ", - "FMINQ", - "FMAXQ", - "FSQRTQ", - "FMADDQ", - "FMSUBQ", - "FNMADDQ", - "FNMSUBQ", - "FCVTWQ", - "FCVTLQ", - "FCVTSQ", - "FCVTDQ", - "FCVTQW", - "FCVTQL", - "FCVTQS", - "FCVTQD", - "FCVTWUQ", - "FCVTLUQ", - "FCVTQWU", - "FCVTQLU", - "FSGNJQ", - "FSGNJNQ", - "FSGNJXQ", - "FMVXQ", - "FMVQX", - "FEQQ", - "FLEQ", - "FLTQ", - "FCLASSQ", - "CSRRW", - "CSRRS", - "CSRRC", - "CSRRWI", - "CSRRSI", - "CSRRCI", - "ECALL", - "SCALL", - "EBREAK", - "SBREAK", - "MRET", - "SRET", - "URET", - "DRET", - "WFI", - "SFENCEVMA", - "HFENCEGVMA", - "HFENCEVVMA", - "WORD", - "BEQZ", - "BGEZ", - "BGT", - "BGTU", - "BGTZ", - "BLE", - "BLEU", - "BLEZ", - "BLTZ", - "BNEZ", - "FNEGD", - "FNEGS", - "FNED", - "FNES", - "MOV", - "MOVB", - "MOVBU", - "MOVF", - "MOVD", - "MOVH", - "MOVHU", - "MOVW", - "MOVWU", - "NEG", - "NEGW", - "NOT", - "SEQZ", - "SNEZ", - "LAST", -} diff --git a/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/cpu.go b/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/cpu.go deleted file mode 100644 index 63a4e9e9d..000000000 --- a/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/cpu.go +++ /dev/null @@ -1,644 +0,0 @@ -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// Portions Copyright © 2019 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" - -//go:generate go run ../stringer.go -i $GOFILE -o anames.go -p riscv - -const ( - // Base register numberings. - REG_X0 = obj.RBaseRISCV + iota - REG_X1 - REG_X2 - REG_X3 - REG_X4 - REG_X5 - REG_X6 - REG_X7 - REG_X8 - REG_X9 - REG_X10 - REG_X11 - REG_X12 - REG_X13 - REG_X14 - REG_X15 - REG_X16 - REG_X17 - REG_X18 - REG_X19 - REG_X20 - REG_X21 - REG_X22 - REG_X23 - REG_X24 - REG_X25 - REG_X26 - REG_X27 - REG_X28 - REG_X29 - REG_X30 - REG_X31 - - // FP register numberings. - REG_F0 - REG_F1 - REG_F2 - REG_F3 - REG_F4 - REG_F5 - REG_F6 - REG_F7 - REG_F8 - REG_F9 - REG_F10 - REG_F11 - REG_F12 - REG_F13 - REG_F14 - REG_F15 - REG_F16 - REG_F17 - REG_F18 - REG_F19 - REG_F20 - REG_F21 - REG_F22 - REG_F23 - REG_F24 - REG_F25 - REG_F26 - REG_F27 - REG_F28 - REG_F29 - REG_F30 - REG_F31 - - // This marks the end of the register numbering. - REG_END - - // General registers reassigned to ABI names. - REG_ZERO = REG_X0 - REG_RA = REG_X1 // aka REG_LR - REG_SP = REG_X2 - REG_GP = REG_X3 // aka REG_SB - REG_TP = REG_X4 // aka REG_G - REG_T0 = REG_X5 - REG_T1 = REG_X6 - REG_T2 = REG_X7 - REG_S0 = REG_X8 - REG_S1 = REG_X9 - REG_A0 = REG_X10 - REG_A1 = REG_X11 - REG_A2 = REG_X12 - REG_A3 = REG_X13 - REG_A4 = REG_X14 - REG_A5 = REG_X15 - REG_A6 = REG_X16 - REG_A7 = REG_X17 - REG_S2 = REG_X18 - REG_S3 = REG_X19 - REG_S4 = REG_X20 // aka REG_CTXT - REG_S5 = REG_X21 - REG_S6 = REG_X22 - REG_S7 = REG_X23 - REG_S8 = REG_X24 - REG_S9 = REG_X25 - REG_S10 = REG_X26 - REG_S11 = REG_X27 - REG_T3 = REG_X28 - REG_T4 = REG_X29 - REG_T5 = REG_X30 - REG_T6 = REG_X31 // aka REG_TMP - - // Go runtime register names. - REG_G = REG_TP // G pointer. - REG_CTXT = REG_S4 // Context for closures. - REG_LR = REG_RA // Link register. - REG_TMP = REG_T6 // Reserved for assembler use. - - // ABI names for floating point registers. - REG_FT0 = REG_F0 - REG_FT1 = REG_F1 - REG_FT2 = REG_F2 - REG_FT3 = REG_F3 - REG_FT4 = REG_F4 - REG_FT5 = REG_F5 - REG_FT6 = REG_F6 - REG_FT7 = REG_F7 - REG_FS0 = REG_F8 - REG_FS1 = REG_F9 - REG_FA0 = REG_F10 - REG_FA1 = REG_F11 - REG_FA2 = REG_F12 - REG_FA3 = REG_F13 - REG_FA4 = REG_F14 - REG_FA5 = REG_F15 - REG_FA6 = REG_F16 - REG_FA7 = REG_F17 - REG_FS2 = REG_F18 - REG_FS3 = REG_F19 - REG_FS4 = REG_F20 - REG_FS5 = REG_F21 - REG_FS6 = REG_F22 - REG_FS7 = REG_F23 - REG_FS8 = REG_F24 - REG_FS9 = REG_F25 - REG_FS10 = REG_F26 - REG_FS11 = REG_F27 - REG_FT8 = REG_F28 - REG_FT9 = REG_F29 - REG_FT10 = REG_F30 - REG_FT11 = REG_F31 - - // Names generated by the SSA compiler. - REGSP = REG_SP - REGG = REG_G -) - -// https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md#dwarf-register-numbers -var RISCV64DWARFRegisters = map[int16]int16{ - // Integer Registers. - REG_X0: 0, - REG_X1: 1, - REG_X2: 2, - REG_X3: 3, - REG_X4: 4, - REG_X5: 5, - REG_X6: 6, - REG_X7: 7, - REG_X8: 8, - REG_X9: 9, - REG_X10: 10, - REG_X11: 11, - REG_X12: 12, - REG_X13: 13, - REG_X14: 14, - REG_X15: 15, - REG_X16: 16, - REG_X17: 17, - REG_X18: 18, - REG_X19: 19, - REG_X20: 20, - REG_X21: 21, - REG_X22: 22, - REG_X23: 23, - REG_X24: 24, - REG_X25: 25, - REG_X26: 26, - REG_X27: 27, - REG_X28: 28, - REG_X29: 29, - REG_X30: 30, - REG_X31: 31, - - // Floating-Point Registers. - REG_F0: 32, - REG_F1: 33, - REG_F2: 34, - REG_F3: 35, - REG_F4: 36, - REG_F5: 37, - REG_F6: 38, - REG_F7: 39, - REG_F8: 40, - REG_F9: 41, - REG_F10: 42, - REG_F11: 43, - REG_F12: 44, - REG_F13: 45, - REG_F14: 46, - REG_F15: 47, - REG_F16: 48, - REG_F17: 49, - REG_F18: 50, - REG_F19: 51, - REG_F20: 52, - REG_F21: 53, - REG_F22: 54, - REG_F23: 55, - REG_F24: 56, - REG_F25: 57, - REG_F26: 58, - REG_F27: 59, - REG_F28: 60, - REG_F29: 61, - REG_F30: 62, - REG_F31: 63, -} - -// Prog.Mark flags. -const ( - // NEED_PCREL_ITYPE_RELOC is set on AUIPC instructions to indicate that - // it is the first instruction in an AUIPC + I-type pair that needs a - // R_RISCV_PCREL_ITYPE relocation. - NEED_PCREL_ITYPE_RELOC = 1 << 0 - - // NEED_PCREL_STYPE_RELOC is set on AUIPC instructions to indicate that - // it is the first instruction in an AUIPC + S-type pair that needs a - // R_RISCV_PCREL_STYPE relocation. - NEED_PCREL_STYPE_RELOC = 1 << 1 -) - -// RISC-V mnemonics, as defined in the "opcodes" and "opcodes-pseudo" files -// from: -// -// https://github.com/riscv/riscv-opcodes -// -// As well as some pseudo-mnemonics (e.g. MOV) used only in the assembler. -// -// See also "The RISC-V Instruction Set Manual" at: -// -// https://riscv.org/specifications/ -// -// If you modify this table, you MUST run 'go generate' to regenerate anames.go! -const ( - // Unprivileged ISA (Document Version 20190608-Base-Ratified) - - // 2.4: Integer Computational Instructions - AADDI = obj.ABaseRISCV + obj.A_ARCHSPECIFIC + iota - ASLTI - ASLTIU - AANDI - AORI - AXORI - ASLLI - ASRLI - ASRAI - ALUI - AAUIPC - AADD - ASLT - ASLTU - AAND - AOR - AXOR - ASLL - ASRL - ASUB - ASRA - - // The SLL/SRL/SRA instructions differ slightly between RV32 and RV64, - // hence there are pseudo-opcodes for the RV32 specific versions. - ASLLIRV32 - ASRLIRV32 - ASRAIRV32 - - // 2.5: Control Transfer Instructions - AJAL - AJALR - ABEQ - ABNE - ABLT - ABLTU - ABGE - ABGEU - - // 2.6: Load and Store Instructions - ALW - ALWU - ALH - ALHU - ALB - ALBU - ASW - ASH - ASB - - // 2.7: Memory Ordering Instructions - AFENCE - AFENCEI - AFENCETSO - - // 5.2: Integer Computational Instructions (RV64I) - AADDIW - ASLLIW - ASRLIW - ASRAIW - AADDW - ASLLW - ASRLW - ASUBW - ASRAW - - // 5.3: Load and Store Instructions (RV64I) - ALD - ASD - - // 7.1: Multiplication Operations - AMUL - AMULH - AMULHU - AMULHSU - AMULW - ADIV - ADIVU - AREM - AREMU - ADIVW - ADIVUW - AREMW - AREMUW - - // 8.2: Load-Reserved/Store-Conditional Instructions - ALRD - ASCD - ALRW - ASCW - - // 8.3: Atomic Memory Operations - AAMOSWAPD - AAMOADDD - AAMOANDD - AAMOORD - AAMOXORD - AAMOMAXD - AAMOMAXUD - AAMOMIND - AAMOMINUD - AAMOSWAPW - AAMOADDW - AAMOANDW - AAMOORW - AAMOXORW - AAMOMAXW - AAMOMAXUW - AAMOMINW - AAMOMINUW - - // 10.1: Base Counters and Timers - ARDCYCLE - ARDCYCLEH - ARDTIME - ARDTIMEH - ARDINSTRET - ARDINSTRETH - - // 11.2: Floating-Point Control and Status Register - AFRCSR - AFSCSR - AFRRM - AFSRM - AFRFLAGS - AFSFLAGS - AFSRMI - AFSFLAGSI - - // 11.5: Single-Precision Load and Store Instructions - AFLW - AFSW - - // 11.6: Single-Precision Floating-Point Computational Instructions - AFADDS - AFSUBS - AFMULS - AFDIVS - AFMINS - AFMAXS - AFSQRTS - AFMADDS - AFMSUBS - AFNMADDS - AFNMSUBS - - // 11.7: Single-Precision Floating-Point Conversion and Move Instructions - AFCVTWS - AFCVTLS - AFCVTSW - AFCVTSL - AFCVTWUS - AFCVTLUS - AFCVTSWU - AFCVTSLU - AFSGNJS - AFSGNJNS - AFSGNJXS - AFMVXS - AFMVSX - AFMVXW - AFMVWX - - // 11.8: Single-Precision Floating-Point Compare Instructions - AFEQS - AFLTS - AFLES - - // 11.9: Single-Precision Floating-Point Classify Instruction - AFCLASSS - - // 12.3: Double-Precision Load and Store Instructions - AFLD - AFSD - - // 12.4: Double-Precision Floating-Point Computational Instructions - AFADDD - AFSUBD - AFMULD - AFDIVD - AFMIND - AFMAXD - AFSQRTD - AFMADDD - AFMSUBD - AFNMADDD - AFNMSUBD - - // 12.5: Double-Precision Floating-Point Conversion and Move Instructions - AFCVTWD - AFCVTLD - AFCVTDW - AFCVTDL - AFCVTWUD - AFCVTLUD - AFCVTDWU - AFCVTDLU - AFCVTSD - AFCVTDS - AFSGNJD - AFSGNJND - AFSGNJXD - AFMVXD - AFMVDX - - // 12.6: Double-Precision Floating-Point Compare Instructions - AFEQD - AFLTD - AFLED - - // 12.7: Double-Precision Floating-Point Classify Instruction - AFCLASSD - - // 13.1 Quad-Precision Load and Store Instructions - AFLQ - AFSQ - - // 13.2: Quad-Precision Computational Instructions - AFADDQ - AFSUBQ - AFMULQ - AFDIVQ - AFMINQ - AFMAXQ - AFSQRTQ - AFMADDQ - AFMSUBQ - AFNMADDQ - AFNMSUBQ - - // 13.3 Quad-Precision Convert and Move Instructions - AFCVTWQ - AFCVTLQ - AFCVTSQ - AFCVTDQ - AFCVTQW - AFCVTQL - AFCVTQS - AFCVTQD - AFCVTWUQ - AFCVTLUQ - AFCVTQWU - AFCVTQLU - AFSGNJQ - AFSGNJNQ - AFSGNJXQ - AFMVXQ - AFMVQX - - // 13.4 Quad-Precision Floating-Point Compare Instructions - AFEQQ - AFLEQ - AFLTQ - - // 13.5 Quad-Precision Floating-Point Classify Instruction - AFCLASSQ - - // Privileged ISA (Version 20190608-Priv-MSU-Ratified) - - // 3.1.9: Instructions to Access CSRs - ACSRRW - ACSRRS - ACSRRC - ACSRRWI - ACSRRSI - ACSRRCI - - // 3.2.1: Environment Call and Breakpoint - AECALL - ASCALL - AEBREAK - ASBREAK - - // 3.2.2: Trap-Return Instructions - AMRET - ASRET - AURET - ADRET - - // 3.2.3: Wait for Interrupt - AWFI - - // 4.2.1: Supervisor Memory-Management Fence Instruction - ASFENCEVMA - - // Hypervisor Memory-Management Instructions - AHFENCEGVMA - AHFENCEVVMA - - // The escape hatch. Inserts a single 32-bit word. - AWORD - - // Pseudo-instructions. These get translated by the assembler into other - // instructions, based on their operands. - ABEQZ - ABGEZ - ABGT - ABGTU - ABGTZ - ABLE - ABLEU - ABLEZ - ABLTZ - ABNEZ - AFNEGD - AFNEGS - AFNED - AFNES - AMOV - AMOVB - AMOVBU - AMOVF - AMOVD - AMOVH - AMOVHU - AMOVW - AMOVWU - ANEG - ANEGW - ANOT - ASEQZ - ASNEZ - - // End marker - ALAST -) - -// All unary instructions which write to their arguments (as opposed to reading -// from them) go here. The assembly parser uses this information to populate -// its AST in a semantically reasonable way. -// -// Any instructions not listed here are assumed to either be non-unary or to read -// from its argument. -var unaryDst = map[obj.As]bool{ - ARDCYCLE: true, - ARDCYCLEH: true, - ARDTIME: true, - ARDTIMEH: true, - ARDINSTRET: true, - ARDINSTRETH: true, -} - -// Instruction encoding masks. -const ( - // ITypeImmMask is a mask including only the immediate portion of - // I-type instructions. - ITypeImmMask = 0xfff00000 - - // STypeImmMask is a mask including only the immediate portion of - // S-type instructions. - STypeImmMask = 0xfe000f80 - - // UTypeImmMask is a mask including only the immediate portion of - // U-type instructions. - UTypeImmMask = 0xfffff000 - - // UJTypeImmMask is a mask including only the immediate portion of - // UJ-type instructions. - UJTypeImmMask = UTypeImmMask -) diff --git a/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/inst.go b/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/inst.go deleted file mode 100644 index 2771ebc0d..000000000 --- a/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/inst.go +++ /dev/null @@ -1,459 +0,0 @@ -// Code generated by parse_opcodes -go; DO NOT EDIT. - -package riscv - -import "github.com/twitchyliquid64/golang-asm/obj" - -type inst struct { - opcode uint32 - funct3 uint32 - rs2 uint32 - csr int64 - funct7 uint32 -} - -func encode(a obj.As) *inst { - switch a { - case ABEQ: - return &inst{0x63, 0x0, 0x0, 0, 0x0} - case ABNE: - return &inst{0x63, 0x1, 0x0, 0, 0x0} - case ABLT: - return &inst{0x63, 0x4, 0x0, 0, 0x0} - case ABGE: - return &inst{0x63, 0x5, 0x0, 0, 0x0} - case ABLTU: - return &inst{0x63, 0x6, 0x0, 0, 0x0} - case ABGEU: - return &inst{0x63, 0x7, 0x0, 0, 0x0} - case AJALR: - return &inst{0x67, 0x0, 0x0, 0, 0x0} - case AJAL: - return &inst{0x6f, 0x0, 0x0, 0, 0x0} - case ALUI: - return &inst{0x37, 0x0, 0x0, 0, 0x0} - case AAUIPC: - return &inst{0x17, 0x0, 0x0, 0, 0x0} - case AADDI: - return &inst{0x13, 0x0, 0x0, 0, 0x0} - case ASLLI: - return &inst{0x13, 0x1, 0x0, 0, 0x0} - case ASLTI: - return &inst{0x13, 0x2, 0x0, 0, 0x0} - case ASLTIU: - return &inst{0x13, 0x3, 0x0, 0, 0x0} - case AXORI: - return &inst{0x13, 0x4, 0x0, 0, 0x0} - case ASRLI: - return &inst{0x13, 0x5, 0x0, 0, 0x0} - case ASRAI: - return &inst{0x13, 0x5, 0x0, 1024, 0x20} - case AORI: - return &inst{0x13, 0x6, 0x0, 0, 0x0} - case AANDI: - return &inst{0x13, 0x7, 0x0, 0, 0x0} - case AADD: - return &inst{0x33, 0x0, 0x0, 0, 0x0} - case ASUB: - return &inst{0x33, 0x0, 0x0, 1024, 0x20} - case ASLL: - return &inst{0x33, 0x1, 0x0, 0, 0x0} - case ASLT: - return &inst{0x33, 0x2, 0x0, 0, 0x0} - case ASLTU: - return &inst{0x33, 0x3, 0x0, 0, 0x0} - case AXOR: - return &inst{0x33, 0x4, 0x0, 0, 0x0} - case ASRL: - return &inst{0x33, 0x5, 0x0, 0, 0x0} - case ASRA: - return &inst{0x33, 0x5, 0x0, 1024, 0x20} - case AOR: - return &inst{0x33, 0x6, 0x0, 0, 0x0} - case AAND: - return &inst{0x33, 0x7, 0x0, 0, 0x0} - case AADDIW: - return &inst{0x1b, 0x0, 0x0, 0, 0x0} - case ASLLIW: - return &inst{0x1b, 0x1, 0x0, 0, 0x0} - case ASRLIW: - return &inst{0x1b, 0x5, 0x0, 0, 0x0} - case ASRAIW: - return &inst{0x1b, 0x5, 0x0, 1024, 0x20} - case AADDW: - return &inst{0x3b, 0x0, 0x0, 0, 0x0} - case ASUBW: - return &inst{0x3b, 0x0, 0x0, 1024, 0x20} - case ASLLW: - return &inst{0x3b, 0x1, 0x0, 0, 0x0} - case ASRLW: - return &inst{0x3b, 0x5, 0x0, 0, 0x0} - case ASRAW: - return &inst{0x3b, 0x5, 0x0, 1024, 0x20} - case ALB: - return &inst{0x3, 0x0, 0x0, 0, 0x0} - case ALH: - return &inst{0x3, 0x1, 0x0, 0, 0x0} - case ALW: - return &inst{0x3, 0x2, 0x0, 0, 0x0} - case ALD: - return &inst{0x3, 0x3, 0x0, 0, 0x0} - case ALBU: - return &inst{0x3, 0x4, 0x0, 0, 0x0} - case ALHU: - return &inst{0x3, 0x5, 0x0, 0, 0x0} - case ALWU: - return &inst{0x3, 0x6, 0x0, 0, 0x0} - case ASB: - return &inst{0x23, 0x0, 0x0, 0, 0x0} - case ASH: - return &inst{0x23, 0x1, 0x0, 0, 0x0} - case ASW: - return &inst{0x23, 0x2, 0x0, 0, 0x0} - case ASD: - return &inst{0x23, 0x3, 0x0, 0, 0x0} - case AFENCE: - return &inst{0xf, 0x0, 0x0, 0, 0x0} - case AFENCEI: - return &inst{0xf, 0x1, 0x0, 0, 0x0} - case AMUL: - return &inst{0x33, 0x0, 0x0, 32, 0x1} - case AMULH: - return &inst{0x33, 0x1, 0x0, 32, 0x1} - case AMULHSU: - return &inst{0x33, 0x2, 0x0, 32, 0x1} - case AMULHU: - return &inst{0x33, 0x3, 0x0, 32, 0x1} - case ADIV: - return &inst{0x33, 0x4, 0x0, 32, 0x1} - case ADIVU: - return &inst{0x33, 0x5, 0x0, 32, 0x1} - case AREM: - return &inst{0x33, 0x6, 0x0, 32, 0x1} - case AREMU: - return &inst{0x33, 0x7, 0x0, 32, 0x1} - case AMULW: - return &inst{0x3b, 0x0, 0x0, 32, 0x1} - case ADIVW: - return &inst{0x3b, 0x4, 0x0, 32, 0x1} - case ADIVUW: - return &inst{0x3b, 0x5, 0x0, 32, 0x1} - case AREMW: - return &inst{0x3b, 0x6, 0x0, 32, 0x1} - case AREMUW: - return &inst{0x3b, 0x7, 0x0, 32, 0x1} - case AAMOADDW: - return &inst{0x2f, 0x2, 0x0, 0, 0x0} - case AAMOXORW: - return &inst{0x2f, 0x2, 0x0, 512, 0x10} - case AAMOORW: - return &inst{0x2f, 0x2, 0x0, 1024, 0x20} - case AAMOANDW: - return &inst{0x2f, 0x2, 0x0, 1536, 0x30} - case AAMOMINW: - return &inst{0x2f, 0x2, 0x0, -2048, 0x40} - case AAMOMAXW: - return &inst{0x2f, 0x2, 0x0, -1536, 0x50} - case AAMOMINUW: - return &inst{0x2f, 0x2, 0x0, -1024, 0x60} - case AAMOMAXUW: - return &inst{0x2f, 0x2, 0x0, -512, 0x70} - case AAMOSWAPW: - return &inst{0x2f, 0x2, 0x0, 128, 0x4} - case ALRW: - return &inst{0x2f, 0x2, 0x0, 256, 0x8} - case ASCW: - return &inst{0x2f, 0x2, 0x0, 384, 0xc} - case AAMOADDD: - return &inst{0x2f, 0x3, 0x0, 0, 0x0} - case AAMOXORD: - return &inst{0x2f, 0x3, 0x0, 512, 0x10} - case AAMOORD: - return &inst{0x2f, 0x3, 0x0, 1024, 0x20} - case AAMOANDD: - return &inst{0x2f, 0x3, 0x0, 1536, 0x30} - case AAMOMIND: - return &inst{0x2f, 0x3, 0x0, -2048, 0x40} - case AAMOMAXD: - return &inst{0x2f, 0x3, 0x0, -1536, 0x50} - case AAMOMINUD: - return &inst{0x2f, 0x3, 0x0, -1024, 0x60} - case AAMOMAXUD: - return &inst{0x2f, 0x3, 0x0, -512, 0x70} - case AAMOSWAPD: - return &inst{0x2f, 0x3, 0x0, 128, 0x4} - case ALRD: - return &inst{0x2f, 0x3, 0x0, 256, 0x8} - case ASCD: - return &inst{0x2f, 0x3, 0x0, 384, 0xc} - case AECALL: - return &inst{0x73, 0x0, 0x0, 0, 0x0} - case AEBREAK: - return &inst{0x73, 0x0, 0x1, 1, 0x0} - case AURET: - return &inst{0x73, 0x0, 0x2, 2, 0x0} - case ASRET: - return &inst{0x73, 0x0, 0x2, 258, 0x8} - case AMRET: - return &inst{0x73, 0x0, 0x2, 770, 0x18} - case ADRET: - return &inst{0x73, 0x0, 0x12, 1970, 0x3d} - case ASFENCEVMA: - return &inst{0x73, 0x0, 0x0, 288, 0x9} - case AWFI: - return &inst{0x73, 0x0, 0x5, 261, 0x8} - case ACSRRW: - return &inst{0x73, 0x1, 0x0, 0, 0x0} - case ACSRRS: - return &inst{0x73, 0x2, 0x0, 0, 0x0} - case ACSRRC: - return &inst{0x73, 0x3, 0x0, 0, 0x0} - case ACSRRWI: - return &inst{0x73, 0x5, 0x0, 0, 0x0} - case ACSRRSI: - return &inst{0x73, 0x6, 0x0, 0, 0x0} - case ACSRRCI: - return &inst{0x73, 0x7, 0x0, 0, 0x0} - case AHFENCEVVMA: - return &inst{0x73, 0x0, 0x0, 544, 0x11} - case AHFENCEGVMA: - return &inst{0x73, 0x0, 0x0, 1568, 0x31} - case AFADDS: - return &inst{0x53, 0x0, 0x0, 0, 0x0} - case AFSUBS: - return &inst{0x53, 0x0, 0x0, 128, 0x4} - case AFMULS: - return &inst{0x53, 0x0, 0x0, 256, 0x8} - case AFDIVS: - return &inst{0x53, 0x0, 0x0, 384, 0xc} - case AFSGNJS: - return &inst{0x53, 0x0, 0x0, 512, 0x10} - case AFSGNJNS: - return &inst{0x53, 0x1, 0x0, 512, 0x10} - case AFSGNJXS: - return &inst{0x53, 0x2, 0x0, 512, 0x10} - case AFMINS: - return &inst{0x53, 0x0, 0x0, 640, 0x14} - case AFMAXS: - return &inst{0x53, 0x1, 0x0, 640, 0x14} - case AFSQRTS: - return &inst{0x53, 0x0, 0x0, 1408, 0x2c} - case AFADDD: - return &inst{0x53, 0x0, 0x0, 32, 0x1} - case AFSUBD: - return &inst{0x53, 0x0, 0x0, 160, 0x5} - case AFMULD: - return &inst{0x53, 0x0, 0x0, 288, 0x9} - case AFDIVD: - return &inst{0x53, 0x0, 0x0, 416, 0xd} - case AFSGNJD: - return &inst{0x53, 0x0, 0x0, 544, 0x11} - case AFSGNJND: - return &inst{0x53, 0x1, 0x0, 544, 0x11} - case AFSGNJXD: - return &inst{0x53, 0x2, 0x0, 544, 0x11} - case AFMIND: - return &inst{0x53, 0x0, 0x0, 672, 0x15} - case AFMAXD: - return &inst{0x53, 0x1, 0x0, 672, 0x15} - case AFCVTSD: - return &inst{0x53, 0x0, 0x1, 1025, 0x20} - case AFCVTDS: - return &inst{0x53, 0x0, 0x0, 1056, 0x21} - case AFSQRTD: - return &inst{0x53, 0x0, 0x0, 1440, 0x2d} - case AFADDQ: - return &inst{0x53, 0x0, 0x0, 96, 0x3} - case AFSUBQ: - return &inst{0x53, 0x0, 0x0, 224, 0x7} - case AFMULQ: - return &inst{0x53, 0x0, 0x0, 352, 0xb} - case AFDIVQ: - return &inst{0x53, 0x0, 0x0, 480, 0xf} - case AFSGNJQ: - return &inst{0x53, 0x0, 0x0, 608, 0x13} - case AFSGNJNQ: - return &inst{0x53, 0x1, 0x0, 608, 0x13} - case AFSGNJXQ: - return &inst{0x53, 0x2, 0x0, 608, 0x13} - case AFMINQ: - return &inst{0x53, 0x0, 0x0, 736, 0x17} - case AFMAXQ: - return &inst{0x53, 0x1, 0x0, 736, 0x17} - case AFCVTSQ: - return &inst{0x53, 0x0, 0x3, 1027, 0x20} - case AFCVTQS: - return &inst{0x53, 0x0, 0x0, 1120, 0x23} - case AFCVTDQ: - return &inst{0x53, 0x0, 0x3, 1059, 0x21} - case AFCVTQD: - return &inst{0x53, 0x0, 0x1, 1121, 0x23} - case AFSQRTQ: - return &inst{0x53, 0x0, 0x0, 1504, 0x2f} - case AFLES: - return &inst{0x53, 0x0, 0x0, -1536, 0x50} - case AFLTS: - return &inst{0x53, 0x1, 0x0, -1536, 0x50} - case AFEQS: - return &inst{0x53, 0x2, 0x0, -1536, 0x50} - case AFLED: - return &inst{0x53, 0x0, 0x0, -1504, 0x51} - case AFLTD: - return &inst{0x53, 0x1, 0x0, -1504, 0x51} - case AFEQD: - return &inst{0x53, 0x2, 0x0, -1504, 0x51} - case AFLEQ: - return &inst{0x53, 0x0, 0x0, -1440, 0x53} - case AFLTQ: - return &inst{0x53, 0x1, 0x0, -1440, 0x53} - case AFEQQ: - return &inst{0x53, 0x2, 0x0, -1440, 0x53} - case AFCVTWS: - return &inst{0x53, 0x0, 0x0, -1024, 0x60} - case AFCVTWUS: - return &inst{0x53, 0x0, 0x1, -1023, 0x60} - case AFCVTLS: - return &inst{0x53, 0x0, 0x2, -1022, 0x60} - case AFCVTLUS: - return &inst{0x53, 0x0, 0x3, -1021, 0x60} - case AFMVXW: - return &inst{0x53, 0x0, 0x0, -512, 0x70} - case AFCLASSS: - return &inst{0x53, 0x1, 0x0, -512, 0x70} - case AFCVTWD: - return &inst{0x53, 0x0, 0x0, -992, 0x61} - case AFCVTWUD: - return &inst{0x53, 0x0, 0x1, -991, 0x61} - case AFCVTLD: - return &inst{0x53, 0x0, 0x2, -990, 0x61} - case AFCVTLUD: - return &inst{0x53, 0x0, 0x3, -989, 0x61} - case AFMVXD: - return &inst{0x53, 0x0, 0x0, -480, 0x71} - case AFCLASSD: - return &inst{0x53, 0x1, 0x0, -480, 0x71} - case AFCVTWQ: - return &inst{0x53, 0x0, 0x0, -928, 0x63} - case AFCVTWUQ: - return &inst{0x53, 0x0, 0x1, -927, 0x63} - case AFCVTLQ: - return &inst{0x53, 0x0, 0x2, -926, 0x63} - case AFCVTLUQ: - return &inst{0x53, 0x0, 0x3, -925, 0x63} - case AFMVXQ: - return &inst{0x53, 0x0, 0x0, -416, 0x73} - case AFCLASSQ: - return &inst{0x53, 0x1, 0x0, -416, 0x73} - case AFCVTSW: - return &inst{0x53, 0x0, 0x0, -768, 0x68} - case AFCVTSWU: - return &inst{0x53, 0x0, 0x1, -767, 0x68} - case AFCVTSL: - return &inst{0x53, 0x0, 0x2, -766, 0x68} - case AFCVTSLU: - return &inst{0x53, 0x0, 0x3, -765, 0x68} - case AFMVWX: - return &inst{0x53, 0x0, 0x0, -256, 0x78} - case AFCVTDW: - return &inst{0x53, 0x0, 0x0, -736, 0x69} - case AFCVTDWU: - return &inst{0x53, 0x0, 0x1, -735, 0x69} - case AFCVTDL: - return &inst{0x53, 0x0, 0x2, -734, 0x69} - case AFCVTDLU: - return &inst{0x53, 0x0, 0x3, -733, 0x69} - case AFMVDX: - return &inst{0x53, 0x0, 0x0, -224, 0x79} - case AFCVTQW: - return &inst{0x53, 0x0, 0x0, -672, 0x6b} - case AFCVTQWU: - return &inst{0x53, 0x0, 0x1, -671, 0x6b} - case AFCVTQL: - return &inst{0x53, 0x0, 0x2, -670, 0x6b} - case AFCVTQLU: - return &inst{0x53, 0x0, 0x3, -669, 0x6b} - case AFMVQX: - return &inst{0x53, 0x0, 0x0, -160, 0x7b} - case AFLW: - return &inst{0x7, 0x2, 0x0, 0, 0x0} - case AFLD: - return &inst{0x7, 0x3, 0x0, 0, 0x0} - case AFLQ: - return &inst{0x7, 0x4, 0x0, 0, 0x0} - case AFSW: - return &inst{0x27, 0x2, 0x0, 0, 0x0} - case AFSD: - return &inst{0x27, 0x3, 0x0, 0, 0x0} - case AFSQ: - return &inst{0x27, 0x4, 0x0, 0, 0x0} - case AFMADDS: - return &inst{0x43, 0x0, 0x0, 0, 0x0} - case AFMSUBS: - return &inst{0x47, 0x0, 0x0, 0, 0x0} - case AFNMSUBS: - return &inst{0x4b, 0x0, 0x0, 0, 0x0} - case AFNMADDS: - return &inst{0x4f, 0x0, 0x0, 0, 0x0} - case AFMADDD: - return &inst{0x43, 0x0, 0x0, 32, 0x1} - case AFMSUBD: - return &inst{0x47, 0x0, 0x0, 32, 0x1} - case AFNMSUBD: - return &inst{0x4b, 0x0, 0x0, 32, 0x1} - case AFNMADDD: - return &inst{0x4f, 0x0, 0x0, 32, 0x1} - case AFMADDQ: - return &inst{0x43, 0x0, 0x0, 96, 0x3} - case AFMSUBQ: - return &inst{0x47, 0x0, 0x0, 96, 0x3} - case AFNMSUBQ: - return &inst{0x4b, 0x0, 0x0, 96, 0x3} - case AFNMADDQ: - return &inst{0x4f, 0x0, 0x0, 96, 0x3} - case ASLLIRV32: - return &inst{0x13, 0x1, 0x0, 0, 0x0} - case ASRLIRV32: - return &inst{0x13, 0x5, 0x0, 0, 0x0} - case ASRAIRV32: - return &inst{0x13, 0x5, 0x0, 1024, 0x20} - case AFRFLAGS: - return &inst{0x73, 0x2, 0x1, 1, 0x0} - case AFSFLAGS: - return &inst{0x73, 0x1, 0x1, 1, 0x0} - case AFSFLAGSI: - return &inst{0x73, 0x5, 0x1, 1, 0x0} - case AFRRM: - return &inst{0x73, 0x2, 0x2, 2, 0x0} - case AFSRM: - return &inst{0x73, 0x1, 0x2, 2, 0x0} - case AFSRMI: - return &inst{0x73, 0x5, 0x2, 2, 0x0} - case AFSCSR: - return &inst{0x73, 0x1, 0x3, 3, 0x0} - case AFRCSR: - return &inst{0x73, 0x2, 0x3, 3, 0x0} - case ARDCYCLE: - return &inst{0x73, 0x2, 0x0, -1024, 0x60} - case ARDTIME: - return &inst{0x73, 0x2, 0x1, -1023, 0x60} - case ARDINSTRET: - return &inst{0x73, 0x2, 0x2, -1022, 0x60} - case ARDCYCLEH: - return &inst{0x73, 0x2, 0x0, -896, 0x64} - case ARDTIMEH: - return &inst{0x73, 0x2, 0x1, -895, 0x64} - case ARDINSTRETH: - return &inst{0x73, 0x2, 0x2, -894, 0x64} - case ASCALL: - return &inst{0x73, 0x0, 0x0, 0, 0x0} - case ASBREAK: - return &inst{0x73, 0x0, 0x1, 1, 0x0} - case AFMVXS: - return &inst{0x53, 0x0, 0x0, -512, 0x70} - case AFMVSX: - return &inst{0x53, 0x0, 0x0, -256, 0x78} - case AFENCETSO: - return &inst{0xf, 0x0, 0x13, -1997, 0x41} - } - return nil -} diff --git a/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/list.go b/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/list.go deleted file mode 100644 index b73f7041d..000000000 --- a/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/list.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package riscv - -import ( - "fmt" - - "github.com/twitchyliquid64/golang-asm/obj" -) - -func init() { - obj.RegisterRegister(obj.RBaseRISCV, REG_END, RegName) - obj.RegisterOpcode(obj.ABaseRISCV, Anames) -} - -func RegName(r int) string { - switch { - case r == 0: - return "NONE" - case r == REG_G: - return "g" - case r == REG_SP: - return "SP" - case REG_X0 <= r && r <= REG_X31: - return fmt.Sprintf("X%d", r-REG_X0) - case REG_F0 <= r && r <= REG_F31: - return fmt.Sprintf("F%d", r-REG_F0) - default: - return fmt.Sprintf("Rgok(%d)", r-obj.RBaseRISCV) - } -} diff --git a/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/obj.go b/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/obj.go deleted file mode 100644 index a9ca000f5..000000000 --- a/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/obj.go +++ /dev/null @@ -1,1999 +0,0 @@ -// 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, -} |