diff options
Diffstat (limited to 'vendor/github.com/twitchyliquid64/golang-asm/obj/riscv')
5 files changed, 3393 insertions, 0 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 new file mode 100644 index 000000000..230a01fe0 --- /dev/null +++ b/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/anames.go @@ -0,0 +1,258 @@ +// 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 new file mode 100644 index 000000000..63a4e9e9d --- /dev/null +++ b/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/cpu.go @@ -0,0 +1,644 @@ +//	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 new file mode 100644 index 000000000..2771ebc0d --- /dev/null +++ b/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/inst.go @@ -0,0 +1,459 @@ +// 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 new file mode 100644 index 000000000..b73f7041d --- /dev/null +++ b/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/list.go @@ -0,0 +1,33 @@ +// 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 new file mode 100644 index 000000000..a9ca000f5 --- /dev/null +++ b/vendor/github.com/twitchyliquid64/golang-asm/obj/riscv/obj.go @@ -0,0 +1,1999 @@ +// 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, +}  | 
