src/cmd/asm/internal/asm/asm.go GO 1,057 lines View on github.com → Search inside
1// Copyright 2014 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package asm67import (8	"fmt"9	"internal/abi"10	"strconv"11	"strings"12	"text/scanner"1314	"cmd/asm/internal/arch"15	"cmd/asm/internal/flags"16	"cmd/asm/internal/lex"17	"cmd/internal/obj"18	"cmd/internal/obj/arm64"19	"cmd/internal/obj/ppc64"20	"cmd/internal/obj/riscv"21	"cmd/internal/obj/x86"22	"cmd/internal/sys"23)2425// TODO: configure the architecture2627var testOut *strings.Builder // Gathers output when testing.2829// append adds the Prog to the end of the program-thus-far.30// If doLabel is set, it also defines the labels collect for this Prog.31func (p *Parser) append(prog *obj.Prog, cond string, doLabel bool) {32	if cond != "" {33		switch p.arch.Family {34		case sys.ARM:35			if !arch.ARMConditionCodes(prog, cond) {36				p.errorf("unrecognized condition code .%q", cond)37				return38			}3940		case sys.ARM64:41			if !arch.ARM64Suffix(prog, cond) {42				p.errorf("unrecognized suffix .%q", cond)43				return44			}4546		case sys.AMD64, sys.I386:47			if err := x86.ParseSuffix(prog, cond); err != nil {48				p.errorf("%v", err)49				return50			}51		case sys.RISCV64:52			if err := riscv.ParseSuffix(prog, cond); err != nil {53				p.errorf("unrecognized suffix .%q", cond)54				return55			}56		default:57			p.errorf("unrecognized suffix .%q", cond)58			return59		}60	}61	if p.firstProg == nil {62		p.firstProg = prog63	} else {64		p.lastProg.Link = prog65	}66	p.lastProg = prog67	if doLabel {68		p.pc++69		for _, label := range p.pendingLabels {70			if p.labels[label] != nil {71				p.errorf("label %q multiply defined", label)72				return73			}74			p.labels[label] = prog75		}76		p.pendingLabels = p.pendingLabels[0:0]77	}78	prog.Pc = p.pc79	if *flags.Debug {80		fmt.Println(p.lineNum, prog)81	}82	if testOut != nil {83		fmt.Fprintln(testOut, prog)84	}85}8687// validSymbol checks that addr represents a valid name for a pseudo-op.88func (p *Parser) validSymbol(pseudo string, addr *obj.Addr, offsetOk bool) bool {89	if addr.Sym == nil || addr.Name != obj.NAME_EXTERN && addr.Name != obj.NAME_STATIC || addr.Scale != 0 || addr.Reg != 0 {90		p.errorf("%s symbol %q must be a symbol(SB)", pseudo, symbolName(addr))91		return false92	}93	if !offsetOk && addr.Offset != 0 {94		p.errorf("%s symbol %q must not be offset from SB", pseudo, symbolName(addr))95		return false96	}97	return true98}99100// evalInteger evaluates an integer constant for a pseudo-op.101func (p *Parser) evalInteger(pseudo string, operands []lex.Token) int64 {102	addr := p.address(operands)103	return p.getConstantPseudo(pseudo, &addr)104}105106// validImmediate checks that addr represents an immediate constant.107func (p *Parser) validImmediate(pseudo string, addr *obj.Addr) bool {108	if addr.Type != obj.TYPE_CONST || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 {109		p.errorf("%s: expected immediate constant; found %s", pseudo, obj.Dconv(&emptyProg, addr))110		return false111	}112	return true113}114115// asmText assembles a TEXT pseudo-op.116// TEXT runtime·sigtramp(SB),4,$0-0117func (p *Parser) asmText(operands [][]lex.Token) {118	if len(operands) != 2 && len(operands) != 3 {119		p.errorf("expect two or three operands for TEXT")120		return121	}122123	// Labels are function scoped. Patch existing labels and124	// create a new label space for this TEXT.125	p.patch()126	p.labels = make(map[string]*obj.Prog)127128	// Operand 0 is the symbol name in the form foo(SB).129	// That means symbol plus indirect on SB and no offset.130	nameAddr := p.address(operands[0])131	if !p.validSymbol("TEXT", &nameAddr, false) {132		return133	}134	name := symbolName(&nameAddr)135	next := 1136137	// Next operand is the optional text flag, a literal integer.138	var flag = int64(0)139	if len(operands) == 3 {140		flag = p.evalInteger("TEXT", operands[1])141		next++142	}143144	// Issue an error if we see a function defined as ABIInternal145	// without NOSPLIT. In ABIInternal, obj needs to know the function146	// signature in order to construct the morestack path, so this147	// currently isn't supported for asm functions.148	if nameAddr.Sym.ABI() == obj.ABIInternal && flag&obj.NOSPLIT == 0 {149		p.errorf("TEXT %q: ABIInternal requires NOSPLIT", name)150	}151152	// Next operand is the frame and arg size.153	// Bizarre syntax: $frameSize-argSize is two words, not subtraction.154	// Both frameSize and argSize must be simple integers; only frameSize155	// can be negative.156	// The "-argSize" may be missing; if so, set it to objabi.ArgsSizeUnknown.157	// Parse left to right.158	op := operands[next]159	if len(op) < 2 || op[0].ScanToken != '$' {160		p.errorf("TEXT %s: frame size must be an immediate constant", name)161		return162	}163	op = op[1:]164	negative := false165	if op[0].ScanToken == '-' {166		negative = true167		op = op[1:]168	}169	if len(op) == 0 || op[0].ScanToken != scanner.Int {170		p.errorf("TEXT %s: frame size must be an immediate constant", name)171		return172	}173	frameSize := p.positiveAtoi(op[0].String())174	if negative {175		frameSize = -frameSize176	}177	op = op[1:]178	argSize := int64(abi.ArgsSizeUnknown)179	if len(op) > 0 {180		// There is an argument size. It must be a minus sign followed by a non-negative integer literal.181		if len(op) != 2 || op[0].ScanToken != '-' || op[1].ScanToken != scanner.Int {182			p.errorf("TEXT %s: argument size must be of form -integer", name)183			return184		}185		argSize = p.positiveAtoi(op[1].String())186	}187	p.ctxt.InitTextSym(nameAddr.Sym, int(flag), p.pos())188	prog := &obj.Prog{189		Ctxt: p.ctxt,190		As:   obj.ATEXT,191		Pos:  p.pos(),192		From: nameAddr,193		To: obj.Addr{194			Type:   obj.TYPE_TEXTSIZE,195			Offset: frameSize,196			// Argsize set below.197		},198	}199	nameAddr.Sym.Func().Text = prog200	prog.To.Val = int32(argSize)201	p.append(prog, "", true)202}203204// asmData assembles a DATA pseudo-op.205// DATA masks<>+0x00(SB)/4, $0x00000000206func (p *Parser) asmData(operands [][]lex.Token) {207	if len(operands) != 2 {208		p.errorf("expect two operands for DATA")209		return210	}211212	// Operand 0 has the general form foo<>+0x04(SB)/4.213	op := operands[0]214	n := len(op)215	if n < 3 || op[n-2].ScanToken != '/' || op[n-1].ScanToken != scanner.Int {216		p.errorf("expect /size for DATA argument")217		return218	}219	szop := op[n-1].String()220	sz, err := strconv.Atoi(szop)221	if err != nil {222		p.errorf("bad size for DATA argument: %q", szop)223	}224	op = op[:n-2]225	nameAddr := p.address(op)226	if !p.validSymbol("DATA", &nameAddr, true) {227		return228	}229	name := symbolName(&nameAddr)230231	// Operand 1 is an immediate constant or address.232	valueAddr := p.address(operands[1])233	switch valueAddr.Type {234	case obj.TYPE_CONST, obj.TYPE_FCONST, obj.TYPE_SCONST, obj.TYPE_ADDR:235		// OK236	default:237		p.errorf("DATA value must be an immediate constant or address")238		return239	}240241	// The addresses must not overlap. Easiest test: require monotonicity.242	if lastAddr, ok := p.dataAddr[name]; ok && nameAddr.Offset < lastAddr {243		p.errorf("overlapping DATA entry for %s", name)244		return245	}246	p.dataAddr[name] = nameAddr.Offset + int64(sz)247248	switch valueAddr.Type {249	case obj.TYPE_CONST:250		switch sz {251		case 1, 2, 4, 8:252			nameAddr.Sym.WriteInt(p.ctxt, nameAddr.Offset, sz, valueAddr.Offset)253		default:254			p.errorf("bad int size for DATA argument: %d", sz)255		}256	case obj.TYPE_FCONST:257		switch sz {258		case 4:259			nameAddr.Sym.WriteFloat32(p.ctxt, nameAddr.Offset, float32(valueAddr.Val.(float64)))260		case 8:261			nameAddr.Sym.WriteFloat64(p.ctxt, nameAddr.Offset, valueAddr.Val.(float64))262		default:263			p.errorf("bad float size for DATA argument: %d", sz)264		}265	case obj.TYPE_SCONST:266		nameAddr.Sym.WriteString(p.ctxt, nameAddr.Offset, sz, valueAddr.Val.(string))267	case obj.TYPE_ADDR:268		if sz == p.arch.PtrSize {269			nameAddr.Sym.WriteAddr(p.ctxt, nameAddr.Offset, sz, valueAddr.Sym, valueAddr.Offset)270		} else {271			p.errorf("bad addr size for DATA argument: %d", sz)272		}273	}274}275276// asmGlobl assembles a GLOBL pseudo-op.277// GLOBL shifts<>(SB),8,$256278// GLOBL shifts<>(SB),$256279func (p *Parser) asmGlobl(operands [][]lex.Token) {280	if len(operands) != 2 && len(operands) != 3 {281		p.errorf("expect two or three operands for GLOBL")282		return283	}284285	// Operand 0 has the general form foo<>+0x04(SB).286	nameAddr := p.address(operands[0])287	if !p.validSymbol("GLOBL", &nameAddr, false) {288		return289	}290	next := 1291292	// Next operand is the optional flag, a literal integer.293	var flag = int64(0)294	if len(operands) == 3 {295		flag = p.evalInteger("GLOBL", operands[1])296		next++297	}298299	// Final operand is an immediate constant.300	addr := p.address(operands[next])301	if !p.validImmediate("GLOBL", &addr) {302		return303	}304305	// log.Printf("GLOBL %s %d, $%d", name, flag, size)306	p.ctxt.GloblPos(nameAddr.Sym, addr.Offset, int(flag), p.pos())307}308309// asmPCData assembles a PCDATA pseudo-op.310// PCDATA $2, $705311func (p *Parser) asmPCData(operands [][]lex.Token) {312	if len(operands) != 2 {313		p.errorf("expect two operands for PCDATA")314		return315	}316317	// Operand 0 must be an immediate constant.318	key := p.address(operands[0])319	if !p.validImmediate("PCDATA", &key) {320		return321	}322323	// Operand 1 must be an immediate constant.324	value := p.address(operands[1])325	if !p.validImmediate("PCDATA", &value) {326		return327	}328329	// log.Printf("PCDATA $%d, $%d", key.Offset, value.Offset)330	prog := &obj.Prog{331		Ctxt: p.ctxt,332		As:   obj.APCDATA,333		Pos:  p.pos(),334		From: key,335		To:   value,336	}337	p.append(prog, "", true)338}339340// asmPCAlign assembles a PCALIGN pseudo-op.341// PCALIGN $16342func (p *Parser) asmPCAlign(operands [][]lex.Token) {343	if len(operands) != 1 {344		p.errorf("expect one operand for PCALIGN")345		return346	}347348	// Operand 0 must be an immediate constant.349	key := p.address(operands[0])350	if !p.validImmediate("PCALIGN", &key) {351		return352	}353354	prog := &obj.Prog{355		Ctxt: p.ctxt,356		As:   obj.APCALIGN,357		Pos:  p.pos(),358		From: key,359	}360	p.append(prog, "", true)361}362363// asmFuncData assembles a FUNCDATA pseudo-op.364// FUNCDATA $1, funcdata<>+4(SB)365func (p *Parser) asmFuncData(operands [][]lex.Token) {366	if len(operands) != 2 {367		p.errorf("expect two operands for FUNCDATA")368		return369	}370371	// Operand 0 must be an immediate constant.372	valueAddr := p.address(operands[0])373	if !p.validImmediate("FUNCDATA", &valueAddr) {374		return375	}376377	// Operand 1 is a symbol name in the form foo(SB).378	nameAddr := p.address(operands[1])379	if !p.validSymbol("FUNCDATA", &nameAddr, true) {380		return381	}382383	prog := &obj.Prog{384		Ctxt: p.ctxt,385		As:   obj.AFUNCDATA,386		Pos:  p.pos(),387		From: valueAddr,388		To:   nameAddr,389	}390	p.append(prog, "", true)391}392393// asmJump assembles a jump instruction.394// JMP	R1395// JMP	exit396// JMP	3(PC)397func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) {398	var target *obj.Addr399	prog := &obj.Prog{400		Ctxt: p.ctxt,401		Pos:  p.pos(),402		As:   op,403	}404	targetAddr := &prog.To405	switch len(a) {406	case 0:407		if p.arch.Family == sys.Wasm {408			target = &obj.Addr{Type: obj.TYPE_NONE}409			break410		}411		p.errorf("wrong number of arguments to %s instruction", op)412		return413	case 1:414		target = &a[0]415	case 2:416		// Special 2-operand jumps.417		if p.arch.Family == sys.ARM64 && arch.IsARM64ADR(op) {418			// ADR label, R. Label is in From.419			target = &a[0]420			prog.To = a[1]421			targetAddr = &prog.From422		} else {423			target = &a[1]424			prog.From = a[0]425		}426	case 3:427		if p.arch.Family == sys.PPC64 {428			// Special 3-operand jumps.429			// a[1] is a register number expressed as a constant or register value430			target = &a[2]431			prog.From = a[0]432			if a[0].Type != obj.TYPE_CONST {433				// Legacy code may use a plain constant, accept it, and coerce434				// into a constant. E.g:435				//   BC 4,...436				// into437				//   BC $4,...438				prog.From = obj.Addr{439					Type:   obj.TYPE_CONST,440					Offset: p.getConstant(prog, op, &a[0]),441				}442443			}444445			// Likewise, fixup usage like:446			//   BC x,LT,...447			//   BC x,foo+2,...448			//   BC x,4449			//   BC x,$5450			// into451			//   BC x,CR0LT,...452			//   BC x,CR0EQ,...453			//   BC x,CR1LT,...454			//   BC x,CR1GT,...455			// The first and second cases demonstrate a symbol name which is456			// effectively discarded. In these cases, the offset determines457			// the CR bit.458			prog.Reg = a[1].Reg459			if a[1].Type != obj.TYPE_REG {460				// The CR bit is represented as a constant 0-31. Convert it to a Reg.461				c := p.getConstant(prog, op, &a[1])462				reg, success := ppc64.ConstantToCRbit(c)463				if !success {464					p.errorf("invalid CR bit register number %d", c)465				}466				prog.Reg = reg467			}468			break469		}470		if p.arch.Family == sys.MIPS || p.arch.Family == sys.MIPS64 || p.arch.Family == sys.RISCV64 {471			// 3-operand jumps.472			// First two must be registers473			target = &a[2]474			prog.From = a[0]475			prog.Reg = p.getRegister(prog, op, &a[1])476			break477		}478		if p.arch.Family == sys.Loong64 {479			// 3-operand jumps.480			// First two must be registers481			target = &a[2]482			prog.From = a[0]483			prog.Reg = p.getRegister(prog, op, &a[1])484			break485		}486		if p.arch.Family == sys.S390X {487			// 3-operand jumps.488			target = &a[2]489			prog.From = a[0]490			if a[1].Reg != 0 {491				// Compare two registers and jump.492				prog.Reg = p.getRegister(prog, op, &a[1])493			} else {494				// Compare register with immediate and jump.495				prog.AddRestSource(a[1])496			}497			break498		}499		if p.arch.Family == sys.ARM64 {500			// Special 3-operand jumps.501			// a[0] must be immediate constant; a[1] is a register.502			if a[0].Type != obj.TYPE_CONST {503				p.errorf("%s: expected immediate constant; found %s", op, obj.Dconv(prog, &a[0]))504				return505			}506			prog.From = a[0]507			prog.Reg = p.getRegister(prog, op, &a[1])508			target = &a[2]509			break510		}511		p.errorf("wrong number of arguments to %s instruction", op)512		return513	case 4:514		if p.arch.Family == sys.S390X || p.arch.Family == sys.PPC64 {515			// 4-operand compare-and-branch.516			prog.From = a[0]517			prog.Reg = p.getRegister(prog, op, &a[1])518			prog.AddRestSource(a[2])519			target = &a[3]520			break521		}522		p.errorf("wrong number of arguments to %s instruction", op)523		return524	default:525		p.errorf("wrong number of arguments to %s instruction", op)526		return527	}528	switch {529	case target.Type == obj.TYPE_BRANCH:530		// JMP 4(PC)531		*targetAddr = obj.Addr{532			Type:   obj.TYPE_BRANCH,533			Offset: p.pc + 1 + target.Offset, // +1 because p.pc is incremented in append, below.534		}535	case target.Type == obj.TYPE_REG:536		// JMP R1537		*targetAddr = *target538	case target.Type == obj.TYPE_MEM && (target.Name == obj.NAME_EXTERN || target.Name == obj.NAME_STATIC):539		// JMP main·morestack(SB)540		*targetAddr = *target541	case target.Type == obj.TYPE_INDIR && (target.Name == obj.NAME_EXTERN || target.Name == obj.NAME_STATIC):542		// JMP *main·morestack(SB)543		*targetAddr = *target544		targetAddr.Type = obj.TYPE_INDIR545	case target.Type == obj.TYPE_MEM && target.Reg == 0 && target.Offset == 0:546		// JMP exit547		if target.Sym == nil {548			// Parse error left name unset.549			return550		}551		targetProg := p.labels[target.Sym.Name]552		if targetProg == nil {553			p.toPatch = append(p.toPatch, Patch{targetAddr, target.Sym.Name})554		} else {555			p.branch(targetAddr, targetProg)556		}557	case target.Type == obj.TYPE_MEM && target.Name == obj.NAME_NONE:558		// JMP 4(R0)559		*targetAddr = *target560		// On the ppc64, 9a encodes BR (CTR) as BR CTR. We do the same.561		if p.arch.Family == sys.PPC64 && target.Offset == 0 {562			targetAddr.Type = obj.TYPE_REG563		}564	case target.Type == obj.TYPE_CONST:565		// JMP $4566		*targetAddr = a[0]567	case target.Type == obj.TYPE_NONE:568		// JMP569	default:570		p.errorf("cannot assemble jump %+v", target)571		return572	}573574	p.append(prog, cond, true)575}576577func (p *Parser) patch() {578	for _, patch := range p.toPatch {579		targetProg := p.labels[patch.label]580		if targetProg == nil {581			p.errorf("undefined label %s", patch.label)582			return583		}584		p.branch(patch.addr, targetProg)585	}586	p.toPatch = p.toPatch[:0]587}588589func (p *Parser) branch(addr *obj.Addr, target *obj.Prog) {590	*addr = obj.Addr{591		Type:  obj.TYPE_BRANCH,592		Index: 0,593	}594	addr.Val = target595}596597func isARM64SVE(op obj.As) bool {598	return op > arm64.ASVESTART599}600601// asmInstruction assembles an instruction.602// MOVW R9, (R10)603func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {604	// fmt.Printf("%s %+v\n", op, a)605	prog := &obj.Prog{606		Ctxt: p.ctxt,607		Pos:  p.pos(),608		As:   op,609	}610	switch len(a) {611	case 0:612		// Nothing to do.613	case 1:614		if p.arch.UnaryDst[op] || op == obj.ARET || op == obj.AGETCALLERPC {615			// prog.From is no address.616			prog.To = a[0]617		} else {618			prog.From = a[0]619			// prog.To is no address.620		}621		if p.arch.Family == sys.PPC64 && arch.IsPPC64NEG(op) {622			// NEG: From and To are both a[0].623			prog.To = a[0]624			prog.From = a[0]625			break626		}627	case 2:628		if p.arch.Family == sys.ARM {629			if arch.IsARMCMP(op) {630				prog.From = a[0]631				prog.Reg = p.getRegister(prog, op, &a[1])632				break633			}634			// Strange special cases.635			if arch.IsARMFloatCmp(op) {636				prog.From = a[0]637				prog.Reg = p.getRegister(prog, op, &a[1])638				break639			}640		} else if p.arch.Family == sys.ARM64 && arch.IsARM64CMP(op) {641			prog.From = a[0]642			prog.Reg = p.getRegister(prog, op, &a[1])643			break644		} else if p.arch.Family == sys.MIPS || p.arch.Family == sys.MIPS64 {645			if arch.IsMIPSCMP(op) || arch.IsMIPSMUL(op) {646				prog.From = a[0]647				prog.Reg = p.getRegister(prog, op, &a[1])648				break649			}650		} else if p.arch.Family == sys.Loong64 {651			if arch.IsLoong64RDTIME(op) {652				// The Loong64 RDTIME family of instructions is a bit special,653				// in that both its register operands are outputs654				prog.To = a[0]655				if a[1].Type != obj.TYPE_REG {656					p.errorf("invalid addressing modes for 2nd operand to %s instruction, must be register", op)657					return658				}659				prog.RegTo2 = a[1].Reg660				break661			}662663			if arch.IsLoong64PRELD(op) {664				prog.From = a[0]665				prog.AddRestSource(a[1])666				break667			}668		} else if p.arch.Family == sys.RISCV64 {669			// RISCV64 pseudo instructions that reference CSRs with symbolic names.670			if isImm, specialIndex, ok := arch.IsRISCV64PseudoCSRO(op); ok {671				if a[0].Type != obj.TYPE_CONST && isImm {672					p.errorf("invalid value for first operand to %s instruction, must be a 5 bit unsigned immediate", op)673					return674				}675				if a[specialIndex].Type != obj.TYPE_SPECIAL {676					p.errorf("invalid value for first or second operand to %s instruction, must be a CSR name", op)677					return678				}679				prog.AddRestSourceArgs([]obj.Addr{a[specialIndex]})680				if specialIndex == 0 {681					prog.To = a[1]682				} else {683					prog.From = a[0]684				}685				break686			}687		}688		prog.From = a[0]689		prog.To = a[1]690	case 3:691		switch p.arch.Family {692		case sys.MIPS, sys.MIPS64:693			prog.From = a[0]694			prog.Reg = p.getRegister(prog, op, &a[1])695			prog.To = a[2]696		case sys.Loong64:697			switch {698			// Loong64 atomic instructions with one input and two outputs.699			case arch.IsLoong64AMO(op):700				prog.From = a[0]701				prog.To = a[1]702				prog.RegTo2 = a[2].Reg703704			case arch.IsLoong64PRELD(op):705				prog.From = a[0]706				prog.AddRestSourceArgs([]obj.Addr{a[1], a[2]})707708			default:709				prog.From = a[0]710				prog.Reg = p.getRegister(prog, op, &a[1])711				prog.To = a[2]712			}713		case sys.ARM:714			// Special cases.715			if arch.IsARMSTREX(op) {716				/*717					STREX x, (y), z718						from=(y) reg=x to=z719				*/720				prog.From = a[1]721				prog.Reg = p.getRegister(prog, op, &a[0])722				prog.To = a[2]723				break724			}725			if arch.IsARMBFX(op) {726				// a[0] and a[1] must be constants, a[2] must be a register727				prog.From = a[0]728				prog.AddRestSource(a[1])729				prog.To = a[2]730				break731			}732			// Otherwise the 2nd operand (a[1]) must be a register.733			prog.From = a[0]734			prog.Reg = p.getRegister(prog, op, &a[1])735			prog.To = a[2]736		case sys.AMD64:737			prog.From = a[0]738			prog.AddRestSource(a[1])739			prog.To = a[2]740		case sys.ARM64:741			switch {742			case arch.IsARM64STLXR(op):743				// ARM64 instructions with one input and two outputs.744				prog.From = a[0]745				prog.To = a[1]746				if a[2].Type != obj.TYPE_REG {747					p.errorf("invalid addressing modes for third operand to %s instruction, must be register", op)748					return749				}750				prog.RegTo2 = a[2].Reg751			case arch.IsARM64TBL(op):752				// one of its inputs does not fit into prog.Reg.753				prog.From = a[0]754				prog.AddRestSource(a[1])755				prog.To = a[2]756			case arch.IsARM64CASP(op):757				prog.From = a[0]758				prog.To = a[1]759				// both 1st operand and 3rd operand are (Rs, Rs+1) register pair.760				// And the register pair must be contiguous.761				if (a[0].Type != obj.TYPE_REGREG) || (a[2].Type != obj.TYPE_REGREG) {762					p.errorf("invalid addressing modes for 1st or 3rd operand to %s instruction, must be register pair", op)763					return764				}765				// For ARM64 CASP-like instructions, its 2nd destination operand is register pair(Rt, Rt+1) that can766				// not fit into prog.RegTo2, so save it to the prog.RestArgs.767				prog.AddRestDest(a[2])768			case isARM64SVE(op):769				// SVE instructions, see arm64/goops_gen.go770				prog.From = a[0]771				prog.AddRestSource(a[1])772				prog.To = a[2]773			default:774				prog.From = a[0]775				prog.Reg = p.getRegister(prog, op, &a[1])776				prog.To = a[2]777			}778		case sys.I386:779			prog.From = a[0]780			prog.AddRestSource(a[1])781			prog.To = a[2]782		case sys.PPC64:783			if arch.IsPPC64CMP(op) {784				// CMPW etc.; third argument is a CR register that goes into prog.Reg.785				prog.From = a[0]786				prog.Reg = p.getRegister(prog, op, &a[2])787				prog.To = a[1]788				break789			}790791			prog.From = a[0]792			prog.To = a[2]793794			// If the second argument is not a register argument, it must be795			// passed RestArgs/AddRestSource796			switch a[1].Type {797			case obj.TYPE_REG:798				prog.Reg = p.getRegister(prog, op, &a[1])799			default:800				prog.AddRestSource(a[1])801			}802		case sys.RISCV64:803			// RISCV64 instructions with one input and two outputs.804			if arch.IsRISCV64AMO(op) {805				prog.From = a[0]806				prog.To = a[1]807				if a[2].Type != obj.TYPE_REG {808					p.errorf("invalid addressing modes for third operand to %s instruction, must be register", op)809					return810				}811				prog.RegTo2 = a[2].Reg812				break813			}814			// RISCV64 instructions that reference CSRs with symbolic names.815			if isImm, ok := arch.IsRISCV64CSRO(op); ok {816				if a[0].Type != obj.TYPE_CONST && isImm {817					p.errorf("invalid value for first operand to %s instruction, must be a 5 bit unsigned immediate", op)818					return819				}820				if a[1].Type != obj.TYPE_SPECIAL {821					p.errorf("invalid value for second operand to %s instruction, must be a CSR name", op)822					return823				}824				prog.AddRestSourceArgs([]obj.Addr{a[1]})825				prog.From = a[0]826				prog.To = a[2]827				break828			}829			prog.From = a[0]830			prog.Reg = p.getRegister(prog, op, &a[1])831			prog.To = a[2]832		case sys.S390X:833			prog.From = a[0]834			if a[1].Type == obj.TYPE_REG {835				prog.Reg = p.getRegister(prog, op, &a[1])836			} else {837				prog.AddRestSource(a[1])838			}839			prog.To = a[2]840		default:841			p.errorf("TODO: implement three-operand instructions for this architecture")842			return843		}844	case 4:845		if p.arch.Family == sys.ARM {846			if arch.IsARMBFX(op) {847				// a[0] and a[1] must be constants, a[2] and a[3] must be registers848				prog.From = a[0]849				prog.AddRestSource(a[1])850				prog.Reg = p.getRegister(prog, op, &a[2])851				prog.To = a[3]852				break853			}854			if arch.IsARMMULA(op) {855				// All must be registers.856				p.getRegister(prog, op, &a[0])857				r1 := p.getRegister(prog, op, &a[1])858				r2 := p.getRegister(prog, op, &a[2])859				p.getRegister(prog, op, &a[3])860				prog.From = a[0]861				prog.To = a[3]862				prog.To.Type = obj.TYPE_REGREG2863				prog.To.Offset = int64(r2)864				prog.Reg = r1865				break866			}867		}868		if p.arch.Family == sys.AMD64 {869			prog.From = a[0]870			prog.AddRestSourceArgs([]obj.Addr{a[1], a[2]})871			prog.To = a[3]872			break873		}874		if p.arch.Family == sys.ARM64 {875			if isARM64SVE(op) {876				// SVE instructions, see arm64/goops_gen.go877				prog.From = a[0]878				prog.AddRestSourceArgs([]obj.Addr{a[1], a[2]})879				prog.To = a[3]880				break881			}882			prog.From = a[0]883			prog.Reg = p.getRegister(prog, op, &a[1])884			prog.AddRestSource(a[2])885			prog.To = a[3]886			break887		}888		if p.arch.Family == sys.Loong64 {889			prog.From = a[0]890			prog.Reg = p.getRegister(prog, op, &a[1])891			prog.AddRestSource(a[2])892			prog.To = a[3]893			break894		}895		if p.arch.Family == sys.PPC64 {896			prog.From = a[0]897			prog.To = a[3]898			// If the second argument is not a register argument, it must be899			// passed RestArgs/AddRestSource900			if a[1].Type == obj.TYPE_REG {901				prog.Reg = p.getRegister(prog, op, &a[1])902				prog.AddRestSource(a[2])903			} else {904				// Don't set prog.Reg if a1 isn't a reg arg.905				prog.AddRestSourceArgs([]obj.Addr{a[1], a[2]})906			}907			break908		}909		if p.arch.Family == sys.RISCV64 {910			prog.From = a[0]911			prog.Reg = p.getRegister(prog, op, &a[1])912			prog.AddRestSource(a[2])913			prog.To = a[3]914			break915		}916		if p.arch.Family == sys.S390X {917			if a[1].Type != obj.TYPE_REG {918				p.errorf("second operand must be a register in %s instruction", op)919				return920			}921			prog.From = a[0]922			prog.Reg = p.getRegister(prog, op, &a[1])923			prog.AddRestSource(a[2])924			prog.To = a[3]925			break926		}927		p.errorf("can't handle %s instruction with 4 operands", op)928		return929	case 5:930		if p.arch.Family == sys.ARM64 && isARM64SVE(op) {931			// SVE instructions, see arm64/goops_gen.go932			prog.From = a[0]933			prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3]})934			prog.To = a[4]935			break936		}937		if p.arch.Family == sys.PPC64 {938			prog.From = a[0]939			// Second arg is always a register type on ppc64.940			prog.Reg = p.getRegister(prog, op, &a[1])941			prog.AddRestSourceArgs([]obj.Addr{a[2], a[3]})942			prog.To = a[4]943			break944		}945		if p.arch.Family == sys.AMD64 {946			prog.From = a[0]947			prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3]})948			prog.To = a[4]949			break950		}951		if p.arch.Family == sys.S390X {952			prog.From = a[0]953			prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3]})954			prog.To = a[4]955			break956		}957		p.errorf("can't handle %s instruction with 5 operands", op)958		return959	case 6:960		if p.arch.Family == sys.ARM64 && isARM64SVE(op) {961			// SVE instructions, see arm64/goops_gen.go962			prog.From = a[0]963			prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3], a[4]})964			prog.To = a[5]965			break966		}967		if p.arch.Family == sys.ARM && arch.IsARMMRC(op) {968			// Strange special case: MCR, MRC.969			prog.To.Type = obj.TYPE_CONST970			x0 := p.getConstant(prog, op, &a[0])971			x1 := p.getConstant(prog, op, &a[1])972			x2 := int64(p.getRegister(prog, op, &a[2]))973			x3 := int64(p.getRegister(prog, op, &a[3]))974			x4 := int64(p.getRegister(prog, op, &a[4]))975			x5 := p.getConstant(prog, op, &a[5])976			// Cond is handled specially for this instruction.977			offset, MRC, ok := arch.ARMMRCOffset(op, cond, x0, x1, x2, x3, x4, x5)978			if !ok {979				p.errorf("unrecognized condition code .%q", cond)980			}981			prog.To.Offset = offset982			cond = ""983			prog.As = MRC // Both instructions are coded as MRC.984			break985		}986		if p.arch.Family == sys.PPC64 {987			prog.From = a[0]988			// Second arg is always a register type on ppc64.989			prog.Reg = p.getRegister(prog, op, &a[1])990			prog.AddRestSourceArgs([]obj.Addr{a[2], a[3], a[4]})991			prog.To = a[5]992			break993		}994		if p.arch.Family == sys.RISCV64 && arch.IsRISCV64VTypeI(op) {995			prog.From = a[0]996			vsew := p.getSpecial(prog, op, &a[1])997			vlmul := p.getSpecial(prog, op, &a[2])998			vtail := p.getSpecial(prog, op, &a[3])999			vmask := p.getSpecial(prog, op, &a[4])1000			if err := arch.RISCV64ValidateVectorType(vsew, vlmul, vtail, vmask); err != nil {1001				p.errorf("invalid vtype: %v", err)1002			}1003			prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3], a[4]})1004			prog.To = a[5]1005			break1006		}1007		fallthrough1008	default:1009		p.errorf("can't handle %s instruction with %d operands", op, len(a))1010		return1011	}10121013	p.append(prog, cond, true)1014}10151016// symbolName returns the symbol name, or an error string if none is available.1017func symbolName(addr *obj.Addr) string {1018	if addr.Sym != nil {1019		return addr.Sym.Name1020	}1021	return "<erroneous symbol>"1022}10231024var emptyProg obj.Prog10251026// getConstantPseudo checks that addr represents a plain constant and returns its value.1027func (p *Parser) getConstantPseudo(pseudo string, addr *obj.Addr) int64 {1028	if addr.Type != obj.TYPE_MEM || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 {1029		p.errorf("%s: expected integer constant; found %s", pseudo, obj.Dconv(&emptyProg, addr))1030	}1031	return addr.Offset1032}10331034// getConstant checks that addr represents a plain constant and returns its value.1035func (p *Parser) getConstant(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 {1036	if addr.Type != obj.TYPE_MEM || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 {1037		p.errorf("%s: expected integer constant; found %s", op, obj.Dconv(prog, addr))1038	}1039	return addr.Offset1040}10411042// getRegister checks that addr represents a register and returns its value.1043func (p *Parser) getRegister(prog *obj.Prog, op obj.As, addr *obj.Addr) int16 {1044	if addr.Type != obj.TYPE_REG || addr.Offset != 0 || addr.Name != 0 || addr.Index != 0 {1045		p.errorf("%s: expected register; found %s", op, obj.Dconv(prog, addr))1046	}1047	return addr.Reg1048}10491050// getSpecial checks that addr represents a special operand and returns its value.1051func (p *Parser) getSpecial(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 {1052	if addr.Type != obj.TYPE_SPECIAL || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 {1053		p.errorf("%s: expected special operand; found %s", op, obj.Dconv(prog, addr))1054	}1055	return addr.Offset1056}

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.