Skip to content
Snippets Groups Projects
asm.go 58.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		}
    	}
    	for n = 0; optab[n].as != obj.AXXX; n++ {
    	}
    	sort.Sort(ocmp(optab[:n]))
    	for i := 0; i < n; i++ {
    		r := optab[i].as
    		r0 := r & obj.AMask
    		start := i
    		for optab[i].as == r {
    			i++
    		}
    		oprange[r0] = optab[start:i]
    		i--
    
    		switch r {
    		default:
    			ctxt.Diag("unknown op in build: %v", r)
    			ctxt.DiagFlush()
    			log.Fatalf("bad code")
    
    		case AABSF:
    			opset(AMOVFD, r0)
    			opset(AMOVDF, r0)
    			opset(AMOVWF, r0)
    			opset(AMOVFW, r0)
    			opset(AMOVWD, r0)
    			opset(AMOVDW, r0)
    			opset(ANEGF, r0)
    			opset(ANEGD, r0)
    			opset(AABSD, r0)
    			opset(ATRUNCDW, r0)
    			opset(ATRUNCFW, r0)
    			opset(ASQRTF, r0)
    			opset(ASQRTD, r0)
    
    		case AMOVVF:
    			opset(AMOVVD, r0)
    			opset(AMOVFV, r0)
    			opset(AMOVDV, r0)
    			opset(ATRUNCDV, r0)
    			opset(ATRUNCFV, r0)
    
    		case AADD:
    			opset(ASGT, r0)
    			opset(ASGTU, r0)
    			opset(AADDU, r0)
    
    		case AADDV:
    			opset(AADDVU, r0)
    
    		case AADDF:
    			opset(ADIVF, r0)
    			opset(ADIVD, r0)
    			opset(AMULF, r0)
    			opset(AMULD, r0)
    			opset(ASUBF, r0)
    			opset(ASUBD, r0)
    			opset(AADDD, r0)
    
    		case AAND:
    			opset(AOR, r0)
    			opset(AXOR, r0)
    
    		case ABEQ:
    			opset(ABNE, r0)
    			opset(ABLT, r0)
    			opset(ABGE, r0)
    			opset(ABGEU, r0)
    			opset(ABLTU, r0)
    
    		case ABLEZ:
    			opset(ABGEZ, r0)
    			opset(ABLTZ, r0)
    			opset(ABGTZ, r0)
    
    		case AMOVB:
    			opset(AMOVH, r0)
    
    		case AMOVBU:
    			opset(AMOVHU, r0)
    
    		case AMUL:
    			opset(AMULU, r0)
    			opset(AMULH, r0)
    			opset(AMULHU, r0)
    			opset(AREM, r0)
    			opset(AREMU, r0)
    			opset(ADIV, r0)
    			opset(ADIVU, r0)
    
    		case AMULV:
    			opset(AMULVU, r0)
    			opset(AMULHV, r0)
    			opset(AMULHVU, r0)
    			opset(AREMV, r0)
    			opset(AREMVU, r0)
    			opset(ADIVV, r0)
    			opset(ADIVVU, r0)
    
    		case ASLL:
    			opset(ASRL, r0)
    			opset(ASRA, r0)
    
    
    		case ASUB:
    			opset(ASUBU, r0)
    			opset(ANOR, r0)
    
    		case ASUBV:
    			opset(ASUBVU, r0)
    
    		case ASYSCALL:
    			opset(ADBAR, r0)
    			opset(ANOOP, r0)
    
    		case ACMPEQF:
    			opset(ACMPGTF, r0)
    			opset(ACMPGTD, r0)
    			opset(ACMPGEF, r0)
    			opset(ACMPGED, r0)
    			opset(ACMPEQD, r0)
    
    		case ABFPT:
    			opset(ABFPF, r0)
    
    		case AMOVWL:
    			opset(AMOVWR, r0)
    
    		case AMOVVL:
    			opset(AMOVVR, r0)
    
    		case AMOVW,
    			AMOVD,
    			AMOVF,
    			AMOVV,
    			ABREAK,
    			ARFE,
    			AJAL,
    			AJMP,
    			AMOVWU,
    			ALL,
    			ALLV,
    			ASC,
    			ASCV,
    			ANEGW,
    			ANEGV,
    			AWORD,
    
    			obj.ANOP,
    			obj.ATEXT,
    			obj.AUNDEF,
    			obj.AFUNCDATA,
    
    			obj.APCDATA,
    			obj.ADUFFZERO,
    			obj.ADUFFCOPY:
    			break
    
    		case ACLO:
    			opset(ACLZ, r0)
    
    		case ATEQ:
    			opset(ATNE, r0)
    
    		}
    	}
    }
    
    func OP(x uint32, y uint32) uint32 {
    	return x<<3 | y<<0
    }
    
    func SP(x uint32, y uint32) uint32 {
    	return x<<29 | y<<26
    }
    
    func OP_TEN(x uint32, y uint32) uint32 {
    	return x<<21 | y<<10
    }
    
    // r1 -> rk
    // r2 -> rj
    // r3 -> rd
    func OP_RRR(op uint32, r1 uint32, r2 uint32, r3 uint32) uint32 {
    	return op | (r1&0x1F)<<10 | (r2&0x1F)<<5 | (r3&0x1F)<<0
    }
    
    // r2 -> rj
    // r3 -> rd
    func OP_RR(op uint32, r2 uint32, r3 uint32) uint32 {
    	return op | (r2&0x1F)<<5 | (r3&0x1F)<<0
    }
    
    func OP_16IR_5I(op uint32, i uint32, r2 uint32) uint32 {
    
    	return op | (i&0xFFFF)<<10 | (r2&0x1F)<<5 | ((i >> 16) & 0x1F)
    
    }
    
    func OP_16IRR(op uint32, i uint32, r2 uint32, r3 uint32) uint32 {
    	return op | (i&0xFFFF)<<10 | (r2&0x1F)<<5 | (r3&0x1F)<<0
    }
    
    func OP_12IRR(op uint32, i uint32, r2 uint32, r3 uint32) uint32 {
    	return op | (i&0xFFF)<<10 | (r2&0x1F)<<5 | (r3&0x1F)<<0
    }
    
    func OP_IR(op uint32, i uint32, r2 uint32) uint32 {
    	return op | (i&0xFFFFF)<<5 | (r2&0x1F)<<0 // ui20, rd5
    }
    
    
    // Encoding for the 'b' or 'bl' instruction.
    
    func OP_B_BL(op uint32, i uint32) uint32 {
    	return op | ((i & 0xFFFF) << 10) | ((i >> 16) & 0x3FF)
    }
    
    func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) {
    	o1 := uint32(0)
    	o2 := uint32(0)
    	o3 := uint32(0)
    	o4 := uint32(0)
    	o5 := uint32(0)
    
    	add := AADDU
    	add = AADDVU
    
    	switch o.type_ {
    	default:
    		c.ctxt.Diag("unknown type %d %v", o.type_)
    		prasm(p)
    
    	case 0: // pseudo ops
    		break
    
    	case 1: // mov r1,r2 ==> OR r1,r0,r2
    		a := AOR
    		if p.As == AMOVW {
    			a = ASLL
    		}
    		o1 = OP_RRR(c.oprrr(a), uint32(REGZERO), uint32(p.From.Reg), uint32(p.To.Reg))
    
    	case 2: // add/sub r1,[r2],r3
    		r := int(p.Reg)
    		if p.As == ANEGW || p.As == ANEGV {
    			r = REGZERO
    		}
    		if r == 0 {
    			r = int(p.To.Reg)
    		}
    		o1 = OP_RRR(c.oprrr(p.As), uint32(p.From.Reg), uint32(r), uint32(p.To.Reg))
    
    	case 3: // mov $soreg, r ==> or/add $i,o,r
    		v := c.regoff(&p.From)
    
    		r := int(p.From.Reg)
    		if r == 0 {
    			r = int(o.param)
    		}
    		a := add
    
    			a = AOR
    		}
    
    		o1 = OP_12IRR(c.opirr(a), uint32(v), uint32(r), uint32(p.To.Reg))
    
    	case 4: // add $scon,[r1],r2
    		v := c.regoff(&p.From)
    
    		r := int(p.Reg)
    		if r == 0 {
    			r = int(p.To.Reg)
    		}
    
    		o1 = OP_12IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.To.Reg))
    
    	case 5: // syscall
    		o1 = c.oprrr(p.As)
    
    	case 6: // beq r1,[r2],sbra
    		v := int32(0)
    		if p.To.Target() != nil {
    			v = int32(p.To.Target().Pc-p.Pc) >> 2
    		}
    
    		as, rd, rj, width := p.As, p.Reg, p.From.Reg, 16
    		switch as {
    		case ABGTZ, ABLEZ:
    
    			width = 21
    			// FCC0 is the implicit source operand, now that we
    			// don't register-allocate from the FCC bank.
    			rd = REG_FCC0
    		case ABEQ, ABNE:
    			if rd == 0 || rd == REGZERO || rj == REGZERO {
    				// BEQZ/BNEZ can be encoded with 21-bit offsets.
    				width = 21
    				as = -as
    				if rj == 0 || rj == REGZERO {
    					rj = rd
    				}
    			}
    		}
    		switch width {
    		case 21:
    
    			if (v<<11)>>11 != v {
    				c.ctxt.Diag("21 bit-width, short branch too far\n%v", p)
    			}
    
    			o1 = OP_16IR_5I(c.opirr(as), uint32(v), uint32(rj))
    		case 16:
    
    			if (v<<16)>>16 != v {
    				c.ctxt.Diag("16 bit-width, short branch too far\n%v", p)
    			}
    
    			o1 = OP_16IRR(c.opirr(as), uint32(v), uint32(rj), uint32(rd))
    		default:
    			c.ctxt.Diag("unexpected branch encoding\n%v", p)
    
    		}
    
    	case 7: // mov r, soreg
    		r := int(p.To.Reg)
    		if r == 0 {
    			r = int(o.param)
    		}
    		v := c.regoff(&p.To)
    		o1 = OP_12IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.From.Reg))
    
    	case 8: // mov soreg, r
    		r := int(p.From.Reg)
    		if r == 0 {
    			r = int(o.param)
    		}
    		v := c.regoff(&p.From)
    		o1 = OP_12IRR(c.opirr(-p.As), uint32(v), uint32(r), uint32(p.To.Reg))
    
    	case 9: // sll r1,[r2],r3
    		if p.As != ACLO && p.As != ACLZ {
    			r := int(p.Reg)
    			if r == 0 {
    				r = int(p.To.Reg)
    			}
    			o1 = OP_RRR(c.oprrr(p.As), uint32(p.From.Reg), uint32(r), uint32(p.To.Reg))
    		} else { // clo r1,r2
    			o1 = OP_RR(c.oprr(p.As), uint32(p.From.Reg), uint32(p.To.Reg))
    		}
    
    	case 10: // add $con,[r1],r2 ==> mov $con, t; add t,[r1],r2
    		v := c.regoff(&p.From)
    		a := AOR
    		if v < 0 {
    			a = AADDU
    		}
    		o1 = OP_12IRR(c.opirr(a), uint32(v), uint32(0), uint32(REGTMP))
    		r := int(p.Reg)
    		if r == 0 {
    			r = int(p.To.Reg)
    		}
    		o2 = OP_RRR(c.oprrr(p.As), uint32(REGTMP), uint32(r), uint32(p.To.Reg))
    
    	case 11: // jmp lbra
    		v := int32(0)
    
    		if p.To.Target() != nil {
    			v = int32(p.To.Target().Pc-p.Pc) >> 2
    
    		}
    		o1 = OP_B_BL(c.opirr(p.As), uint32(v))
    		if p.To.Sym == nil {
    
    			p.To.Sym = c.cursym.Func().Text.From.Sym
    			p.To.Offset = p.To.Target().Pc
    		}
    		rel := obj.Addrel(c.cursym)
    		rel.Off = int32(c.pc)
    		rel.Siz = 4
    		rel.Sym = p.To.Sym
    		rel.Add = p.To.Offset
    		rel.Type = objabi.R_CALLLOONG64
    
    	case 12: // movbs r,r
    		// NOTE: this case does not use REGTMP. If it ever does,
    		// remove the NOTUSETMP flag in optab.
    		v := 16
    		if p.As == AMOVB {
    			v = 24
    		}
    		o1 = OP_16IRR(c.opirr(ASLL), uint32(v), uint32(p.From.Reg), uint32(p.To.Reg))
    		o2 = OP_16IRR(c.opirr(ASRA), uint32(v), uint32(p.To.Reg), uint32(p.To.Reg))
    
    	case 13: // movbu r,r
    		if p.As == AMOVBU {
    			o1 = OP_12IRR(c.opirr(AAND), uint32(0xff), uint32(p.From.Reg), uint32(p.To.Reg))
    		} else {
    			// bstrpick.d (msbd=15, lsbd=0)
    			o1 = (0x33c0 << 10) | ((uint32(p.From.Reg) & 0x1f) << 5) | (uint32(p.To.Reg) & 0x1F)
    		}
    
    	case 14: // movwu r,r
    		// NOTE: this case does not use REGTMP. If it ever does,
    		// remove the NOTUSETMP flag in optab.
    		o1 = OP_16IRR(c.opirr(-ASLLV), uint32(32)&0x3f, uint32(p.From.Reg), uint32(p.To.Reg))
    		o2 = OP_16IRR(c.opirr(-ASRLV), uint32(32)&0x3f, uint32(p.To.Reg), uint32(p.To.Reg))
    
    	case 15: // teq $c r,r
    		v := c.regoff(&p.From)
    		r := int(p.Reg)
    		if r == 0 {
    			r = REGZERO
    		}
    		/*
    			teq c, r1, r2
    			fallthrough
    			==>
    			bne r1, r2, 2
    			break c
    			fallthrough
    		*/
    		if p.As == ATEQ {
    			o1 = OP_16IRR(c.opirr(ABNE), uint32(2), uint32(r), uint32(p.To.Reg))
    		} else { // ATNE
    			o1 = OP_16IRR(c.opirr(ABEQ), uint32(2), uint32(r), uint32(p.To.Reg))
    		}
    		o2 = c.oprrr(ABREAK) | (uint32(v) & 0x7FFF)
    
    	case 16: // sll $c,[r1],r2
    		v := c.regoff(&p.From)
    		r := int(p.Reg)
    		if r == 0 {
    			r = int(p.To.Reg)
    		}
    
    		// instruction ending with V:6-digit immediate, others:5-digit immediate
    		if v >= 32 && vshift(p.As) {
    			o1 = OP_16IRR(c.opirr(p.As), uint32(v)&0x3f, uint32(r), uint32(p.To.Reg))
    		} else {
    			o1 = OP_16IRR(c.opirr(p.As), uint32(v)&0x1f, uint32(r), uint32(p.To.Reg))
    		}
    
    	case 17:
    		o1 = OP_RRR(c.oprrr(p.As), uint32(REGZERO), uint32(p.From.Reg), uint32(p.To.Reg))
    
    	case 18: // jmp [r1],0(r2)
    		r := int(p.Reg)
    		if r == 0 {
    			r = int(o.param)
    		}
    		o1 = OP_RRR(c.oprrr(p.As), uint32(0), uint32(p.To.Reg), uint32(r))
    		if p.As == obj.ACALL {
    			rel := obj.Addrel(c.cursym)
    			rel.Off = int32(c.pc)
    			rel.Siz = 0
    			rel.Type = objabi.R_CALLIND
    		}
    
    	case 19: // mov $lcon,r
    		// NOTE: this case does not use REGTMP. If it ever does,
    		// remove the NOTUSETMP flag in optab.
    		v := c.regoff(&p.From)
    		o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(p.To.Reg))
    		o2 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(p.To.Reg), uint32(p.To.Reg))
    
    	case 23: // add $lcon,r1,r2
    		v := c.regoff(&p.From)
    		o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(REGTMP))
    		o2 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(REGTMP), uint32(REGTMP))
    		r := int(p.Reg)
    		if r == 0 {
    			r = int(p.To.Reg)
    		}
    		o3 = OP_RRR(c.oprrr(p.As), uint32(REGTMP), uint32(r), uint32(p.To.Reg))
    
    	case 24: // mov $ucon,r
    		v := c.regoff(&p.From)
    		o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(p.To.Reg))
    
    	case 25: // add/and $ucon,[r1],r2
    		v := c.regoff(&p.From)
    		o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(REGTMP))
    		r := int(p.Reg)
    		if r == 0 {
    			r = int(p.To.Reg)
    		}
    		o2 = OP_RRR(c.oprrr(p.As), uint32(REGTMP), uint32(r), uint32(p.To.Reg))
    
    	case 26: // mov $lsext/auto/oreg,r
    		v := c.regoff(&p.From)
    		o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(REGTMP))
    		o2 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(REGTMP), uint32(REGTMP))
    		r := int(p.From.Reg)
    		if r == 0 {
    			r = int(o.param)
    		}
    		o3 = OP_RRR(c.oprrr(add), uint32(REGTMP), uint32(r), uint32(p.To.Reg))
    
    	case 27: // mov [sl]ext/auto/oreg,fr
    		v := c.regoff(&p.From)
    		r := int(p.From.Reg)
    		if r == 0 {
    			r = int(o.param)
    		}
    		a := -AMOVF
    		if p.As == AMOVD {
    			a = -AMOVD
    		}
    		switch o.size {
    		case 12:
    			o1 = OP_IR(c.opir(ALU12IW), uint32((v+1<<11)>>12), uint32(REGTMP))
    			o2 = OP_RRR(c.oprrr(add), uint32(r), uint32(REGTMP), uint32(REGTMP))
    			o3 = OP_12IRR(c.opirr(a), uint32(v), uint32(REGTMP), uint32(p.To.Reg))
    
    		case 4:
    			o1 = OP_12IRR(c.opirr(a), uint32(v), uint32(r), uint32(p.To.Reg))
    		}
    
    	case 28: // mov fr,[sl]ext/auto/oreg
    		v := c.regoff(&p.To)
    		r := int(p.To.Reg)
    		if r == 0 {
    			r = int(o.param)
    		}
    		a := AMOVF
    		if p.As == AMOVD {
    			a = AMOVD
    		}
    		switch o.size {
    		case 12:
    			o1 = OP_IR(c.opir(ALU12IW), uint32((v+1<<11)>>12), uint32(REGTMP))
    			o2 = OP_RRR(c.oprrr(add), uint32(r), uint32(REGTMP), uint32(REGTMP))
    			o3 = OP_12IRR(c.opirr(a), uint32(v), uint32(REGTMP), uint32(p.From.Reg))
    
    		case 4:
    			o1 = OP_12IRR(c.opirr(a), uint32(v), uint32(r), uint32(p.From.Reg))
    		}
    
    	case 30: // movw r,fr
    		a := OP_TEN(8, 1321) // movgr2fr.w
    		o1 = OP_RR(a, uint32(p.From.Reg), uint32(p.To.Reg))
    
    	case 31: // movw fr,r
    		a := OP_TEN(8, 1325) // movfr2gr.s
    		o1 = OP_RR(a, uint32(p.From.Reg), uint32(p.To.Reg))
    
    	case 32: // fadd fr1,[fr2],fr3
    		r := int(p.Reg)
    		if r == 0 {
    			r = int(p.To.Reg)
    		}
    		o1 = OP_RRR(c.oprrr(p.As), uint32(p.From.Reg), uint32(r), uint32(p.To.Reg))
    
    	case 33: // fabs fr1, fr3
    		o1 = OP_RRR(c.oprrr(p.As), uint32(0), uint32(p.From.Reg), uint32(p.To.Reg))
    
    	case 34: // mov $con,fr
    		v := c.regoff(&p.From)
    		a := AADDU
    
    			a = AOR
    		}
    		o1 = OP_12IRR(c.opirr(a), uint32(v), uint32(0), uint32(REGTMP))
    		o2 = OP_RR(OP_TEN(8, 1321), uint32(REGTMP), uint32(p.To.Reg)) // movgr2fr.w
    
    	case 35: // mov r,lext/auto/oreg
    		v := c.regoff(&p.To)
    		r := int(p.To.Reg)
    		if r == 0 {
    			r = int(o.param)
    		}
    		o1 = OP_IR(c.opir(ALU12IW), uint32((v+1<<11)>>12), uint32(REGTMP))
    		o2 = OP_RRR(c.oprrr(add), uint32(r), uint32(REGTMP), uint32(REGTMP))
    		o3 = OP_12IRR(c.opirr(p.As), uint32(v), uint32(REGTMP), uint32(p.From.Reg))
    
    	case 36: // mov lext/auto/oreg,r
    		v := c.regoff(&p.From)
    		r := int(p.From.Reg)
    		if r == 0 {
    			r = int(o.param)
    		}
    		o1 = OP_IR(c.opir(ALU12IW), uint32((v+1<<11)>>12), uint32(REGTMP))
    		o2 = OP_RRR(c.oprrr(add), uint32(r), uint32(REGTMP), uint32(REGTMP))
    		o3 = OP_12IRR(c.opirr(-p.As), uint32(v), uint32(REGTMP), uint32(p.To.Reg))
    
    	case 40: // word
    		o1 = uint32(c.regoff(&p.From))
    
    	case 47: // movv r,fr
    		a := OP_TEN(8, 1322) // movgr2fr.d
    		o1 = OP_RR(a, uint32(p.From.Reg), uint32(p.To.Reg))
    
    	case 48: // movv fr,r
    		a := OP_TEN(8, 1326) // movfr2gr.d
    		o1 = OP_RR(a, uint32(p.From.Reg), uint32(p.To.Reg))
    
    	case 49: // undef
    		o1 = c.oprrr(ABREAK)
    
    	// relocation operations
    
    	case 50: // mov r,addr ==> pcalau12i + sw
    		o1 = OP_IR(c.opir(APCALAU12I), uint32(0), uint32(REGTMP))
    
    		rel := obj.Addrel(c.cursym)
    		rel.Off = int32(c.pc)
    		rel.Siz = 4
    		rel.Sym = p.To.Sym
    		rel.Add = p.To.Offset
    		rel.Type = objabi.R_ADDRLOONG64U
    
    		o2 = OP_12IRR(c.opirr(p.As), uint32(0), uint32(REGTMP), uint32(p.From.Reg))
    		rel2 := obj.Addrel(c.cursym)
    		rel2.Off = int32(c.pc + 4)
    		rel2.Siz = 4
    		rel2.Sym = p.To.Sym
    		rel2.Add = p.To.Offset
    		rel2.Type = objabi.R_ADDRLOONG64
    
    
    	case 51: // mov addr,r ==> pcalau12i + lw
    		o1 = OP_IR(c.opir(APCALAU12I), uint32(0), uint32(REGTMP))
    
    		rel := obj.Addrel(c.cursym)
    		rel.Off = int32(c.pc)
    		rel.Siz = 4
    		rel.Sym = p.From.Sym
    		rel.Add = p.From.Offset
    		rel.Type = objabi.R_ADDRLOONG64U
    		o2 = OP_12IRR(c.opirr(-p.As), uint32(0), uint32(REGTMP), uint32(p.To.Reg))
    		rel2 := obj.Addrel(c.cursym)
    		rel2.Off = int32(c.pc + 4)
    		rel2.Siz = 4
    		rel2.Sym = p.From.Sym
    		rel2.Add = p.From.Offset
    		rel2.Type = objabi.R_ADDRLOONG64
    
    	case 52: // mov $lext, r
    		// NOTE: this case does not use REGTMP. If it ever does,
    		// remove the NOTUSETMP flag in optab.
    
    		o1 = OP_IR(c.opir(APCALAU12I), uint32(0), uint32(p.To.Reg))
    
    		rel := obj.Addrel(c.cursym)
    		rel.Off = int32(c.pc)
    		rel.Siz = 4
    		rel.Sym = p.From.Sym
    		rel.Add = p.From.Offset
    		rel.Type = objabi.R_ADDRLOONG64U
    		o2 = OP_12IRR(c.opirr(add), uint32(0), uint32(p.To.Reg), uint32(p.To.Reg))
    		rel2 := obj.Addrel(c.cursym)
    		rel2.Off = int32(c.pc + 4)
    		rel2.Siz = 4
    		rel2.Sym = p.From.Sym
    		rel2.Add = p.From.Offset
    		rel2.Type = objabi.R_ADDRLOONG64
    
    	case 53: // mov r, tlsvar ==>  lu12i.w + ori + add r2, regtmp + sw o(regtmp)
    		// NOTE: this case does not use REGTMP. If it ever does,
    		// remove the NOTUSETMP flag in optab.
    		o1 = OP_IR(c.opir(ALU12IW), uint32(0), uint32(REGTMP))
    		rel := obj.Addrel(c.cursym)
    		rel.Off = int32(c.pc)
    		rel.Siz = 4
    		rel.Sym = p.To.Sym
    		rel.Add = p.To.Offset
    		rel.Type = objabi.R_ADDRLOONG64TLSU
    		o2 = OP_12IRR(c.opirr(AOR), uint32(0), uint32(REGTMP), uint32(REGTMP))
    		rel2 := obj.Addrel(c.cursym)
    		rel2.Off = int32(c.pc + 4)
    		rel2.Siz = 4
    		rel2.Sym = p.To.Sym
    		rel2.Add = p.To.Offset
    		rel2.Type = objabi.R_ADDRLOONG64TLS
    		o3 = OP_RRR(c.oprrr(AADDV), uint32(REG_R2), uint32(REGTMP), uint32(REGTMP))
    		o4 = OP_12IRR(c.opirr(p.As), uint32(0), uint32(REGTMP), uint32(p.From.Reg))
    
    	case 54: // lu12i.w + ori + add r2, regtmp + lw o(regtmp)
    		// NOTE: this case does not use REGTMP. If it ever does,
    		// remove the NOTUSETMP flag in optab.
    		o1 = OP_IR(c.opir(ALU12IW), uint32(0), uint32(REGTMP))
    		rel := obj.Addrel(c.cursym)
    		rel.Off = int32(c.pc)
    		rel.Siz = 4
    		rel.Sym = p.From.Sym
    		rel.Add = p.From.Offset
    		rel.Type = objabi.R_ADDRLOONG64TLSU
    		o2 = OP_12IRR(c.opirr(AOR), uint32(0), uint32(REGTMP), uint32(REGTMP))
    		rel2 := obj.Addrel(c.cursym)
    		rel2.Off = int32(c.pc + 4)
    		rel2.Siz = 4
    		rel2.Sym = p.From.Sym
    		rel2.Add = p.From.Offset
    		rel2.Type = objabi.R_ADDRLOONG64TLS
    		o3 = OP_RRR(c.oprrr(AADDV), uint32(REG_R2), uint32(REGTMP), uint32(REGTMP))
    		o4 = OP_12IRR(c.opirr(-p.As), uint32(0), uint32(REGTMP), uint32(p.To.Reg))
    
    	case 55: //  lu12i.w + ori + add r2, regtmp
    		// NOTE: this case does not use REGTMP. If it ever does,
    		// remove the NOTUSETMP flag in optab.
    		o1 = OP_IR(c.opir(ALU12IW), uint32(0), uint32(REGTMP))
    		rel := obj.Addrel(c.cursym)
    		rel.Off = int32(c.pc)
    		rel.Siz = 4
    		rel.Sym = p.From.Sym
    		rel.Add = p.From.Offset
    		rel.Type = objabi.R_ADDRLOONG64TLSU
    		o2 = OP_12IRR(c.opirr(AOR), uint32(0), uint32(REGTMP), uint32(REGTMP))
    		rel2 := obj.Addrel(c.cursym)
    		rel2.Off = int32(c.pc + 4)
    		rel2.Siz = 4
    		rel2.Sym = p.From.Sym
    		rel2.Add = p.From.Offset
    		rel2.Type = objabi.R_ADDRLOONG64TLS
    		o3 = OP_RRR(c.oprrr(AADDV), uint32(REG_R2), uint32(REGTMP), uint32(p.To.Reg))
    
    
    	case 56: // mov r, tlsvar IE model ==> (pcalau12i + ld.d)tlsvar@got + add.d + st.d
    		o1 = OP_IR(c.opir(APCALAU12I), uint32(0), uint32(REGTMP))
    		rel := obj.Addrel(c.cursym)
    		rel.Off = int32(c.pc)
    		rel.Siz = 4
    		rel.Sym = p.To.Sym
    		rel.Add = 0x0
    		rel.Type = objabi.R_LOONG64_TLS_IE_PCREL_HI
    		o2 = OP_12IRR(c.opirr(-p.As), uint32(0), uint32(REGTMP), uint32(REGTMP))
    		rel2 := obj.Addrel(c.cursym)
    		rel2.Off = int32(c.pc + 4)
    		rel2.Siz = 4
    		rel2.Sym = p.To.Sym
    		rel2.Add = 0x0
    		rel2.Type = objabi.R_LOONG64_TLS_IE_LO
    		o3 = OP_RRR(c.oprrr(AADDVU), uint32(REGTMP), uint32(REG_R2), uint32(REGTMP))
    		o4 = OP_12IRR(c.opirr(p.As), uint32(0), uint32(REGTMP), uint32(p.From.Reg))
    
    	case 57: // mov tlsvar, r IE model ==> (pcalau12i + ld.d)tlsvar@got + add.d + ld.d
    		o1 = OP_IR(c.opir(APCALAU12I), uint32(0), uint32(REGTMP))
    		rel := obj.Addrel(c.cursym)
    		rel.Off = int32(c.pc)
    		rel.Siz = 4
    		rel.Sym = p.From.Sym
    		rel.Add = 0x0
    		rel.Type = objabi.R_LOONG64_TLS_IE_PCREL_HI
    		o2 = OP_12IRR(c.opirr(-p.As), uint32(0), uint32(REGTMP), uint32(REGTMP))
    		rel2 := obj.Addrel(c.cursym)
    		rel2.Off = int32(c.pc + 4)
    		rel2.Siz = 4
    		rel2.Sym = p.From.Sym
    		rel2.Add = 0x0
    		rel2.Type = objabi.R_LOONG64_TLS_IE_LO
    		o3 = OP_RRR(c.oprrr(AADDVU), uint32(REGTMP), uint32(REG_R2), uint32(REGTMP))
    		o4 = OP_12IRR(c.opirr(-p.As), uint32(0), uint32(REGTMP), uint32(p.To.Reg))
    
    
    	case 59: // mov $dcon,r
    		// NOTE: this case does not use REGTMP. If it ever does,
    		// remove the NOTUSETMP flag in optab.
    		v := c.vregoff(&p.From)
    		o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(p.To.Reg))
    		o2 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(p.To.Reg), uint32(p.To.Reg))
    		o3 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(p.To.Reg))
    		o4 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(p.To.Reg), uint32(p.To.Reg))
    
    	case 60: // add $dcon,r1,r2
    		v := c.vregoff(&p.From)
    		o1 = OP_IR(c.opir(ALU12IW), uint32(v>>12), uint32(REGTMP))
    		o2 = OP_12IRR(c.opirr(AOR), uint32(v), uint32(REGTMP), uint32(REGTMP))
    		o3 = OP_IR(c.opir(ALU32ID), uint32(v>>32), uint32(REGTMP))
    		o4 = OP_12IRR(c.opirr(ALU52ID), uint32(v>>52), uint32(REGTMP), uint32(REGTMP))
    		r := int(p.Reg)
    		if r == 0 {
    			r = int(p.To.Reg)
    		}
    		o5 = OP_RRR(c.oprrr(p.As), uint32(REGTMP), uint32(r), uint32(p.To.Reg))
    
    	case 61: // word C_DCON
    		o1 = uint32(c.vregoff(&p.From))
    		o2 = uint32(c.vregoff(&p.From) >> 32)
    
    
    	case 62: // rdtimex rd, rj
    		o1 = OP_RR(c.oprr(p.As), uint32(p.To.Reg), uint32(p.RegTo2))
    
    
    	case 63: // movv c_fcc0, c_reg ==> movcf2gr rd, cj
    		a := OP_TEN(8, 1335)
    		o1 = OP_RR(a, uint32(p.From.Reg), uint32(p.To.Reg))
    
    	case 64: // movv c_reg, c_fcc0 ==> movgr2cf cd, rj
    		a := OP_TEN(8, 1334)
    		o1 = OP_RR(a, uint32(p.From.Reg), uint32(p.To.Reg))
    
    
    	case 65: // mov sym@GOT, r ==> pcalau12i + ld.d
    		o1 = OP_IR(c.opir(APCALAU12I), uint32(0), uint32(p.To.Reg))
    		rel := obj.Addrel(c.cursym)
    		rel.Off = int32(c.pc)
    		rel.Siz = 4
    		rel.Sym = p.From.Sym
    		rel.Type = objabi.R_LOONG64_GOT_HI
    		rel.Add = 0x0
    		o2 = OP_12IRR(c.opirr(-p.As), uint32(0), uint32(p.To.Reg), uint32(p.To.Reg))
    		rel2 := obj.Addrel(c.cursym)
    		rel2.Off = int32(c.pc + 4)
    		rel2.Siz = 4
    		rel2.Sym = p.From.Sym
    		rel2.Type = objabi.R_LOONG64_GOT_LO
    		rel2.Add = 0x0
    
    	}
    
    	out[0] = o1
    	out[1] = o2
    	out[2] = o3
    	out[3] = o4
    	out[4] = o5
    }
    
    func (c *ctxt0) vregoff(a *obj.Addr) int64 {
    	c.instoffset = 0
    	c.aclass(a)
    	return c.instoffset
    }
    
    func (c *ctxt0) regoff(a *obj.Addr) int32 {
    	return int32(c.vregoff(a))
    }
    
    func (c *ctxt0) oprrr(a obj.As) uint32 {
    	switch a {
    	case AADD:
    		return 0x20 << 15
    	case AADDU:
    		return 0x20 << 15
    	case ASGT:
    		return 0x24 << 15 // SLT
    	case ASGTU:
    		return 0x25 << 15 // SLTU
    
    	case AMASKEQZ:
    		return 0x26 << 15
    	case AMASKNEZ:
    		return 0x27 << 15
    
    	case AAND:
    		return 0x29 << 15
    	case AOR:
    		return 0x2a << 15
    	case AXOR:
    		return 0x2b << 15
    	case ASUB:
    		return 0x22 << 15
    	case ASUBU, ANEGW:
    		return 0x22 << 15
    	case ANOR:
    		return 0x28 << 15
    	case ASLL:
    		return 0x2e << 15
    	case ASRL:
    		return 0x2f << 15
    	case ASRA:
    		return 0x30 << 15
    
    	case ASLLV:
    		return 0x31 << 15
    	case ASRLV:
    		return 0x32 << 15
    	case ASRAV:
    		return 0x33 << 15
    
    	case AADDV:
    		return 0x21 << 15
    	case AADDVU:
    		return 0x21 << 15
    	case ASUBV:
    		return 0x23 << 15
    	case ASUBVU, ANEGV:
    		return 0x23 << 15
    
    	case AMUL:
    		return 0x38 << 15 // mul.w
    	case AMULU:
    		return 0x38 << 15 // mul.w
    	case AMULH:
    		return 0x39 << 15 // mulh.w
    	case AMULHU:
    		return 0x3a << 15 // mulhu.w
    	case AMULV:
    		return 0x3b << 15 // mul.d
    	case AMULVU:
    		return 0x3b << 15 // mul.d
    	case AMULHV:
    		return 0x3c << 15 // mulh.d
    	case AMULHVU:
    		return 0x3d << 15 // mulhu.d
    	case ADIV:
    		return 0x40 << 15 // div.w
    	case ADIVU:
    		return 0x42 << 15 // div.wu
    	case ADIVV:
    		return 0x44 << 15 // div.d
    	case ADIVVU:
    		return 0x46 << 15 // div.du
    	case AREM:
    		return 0x41 << 15 // mod.w
    	case AREMU:
    		return 0x43 << 15 // mod.wu
    	case AREMV:
    		return 0x45 << 15 // mod.d
    	case AREMVU:
    		return 0x47 << 15 // mod.du
    
    	case AJMP:
    		return 0x13 << 26 // jirl r0, rj, 0
    	case AJAL:
    		return (0x13 << 26) | 1 // jirl r1, rj, 0
    
    	case ABREAK:
    		return 0x54 << 15
    	case ASYSCALL:
    		return 0x56 << 15
    	case ADIVF:
    		return 0x20d << 15
    	case ADIVD:
    		return 0x20e << 15
    	case AMULF:
    		return 0x209 << 15
    	case AMULD:
    		return 0x20a << 15
    	case ASUBF:
    		return 0x205 << 15
    	case ASUBD:
    		return 0x206 << 15
    	case AADDF:
    		return 0x201 << 15
    	case AADDD:
    		return 0x202 << 15
    	case ATRUNCFV:
    		return 0x46a9 << 10
    	case ATRUNCDV:
    		return 0x46aa << 10
    	case ATRUNCFW:
    		return 0x46a1 << 10
    	case ATRUNCDW:
    		return 0x46a2 << 10
    	case AMOVFV:
    		return 0x46c9 << 10
    	case AMOVDV:
    		return 0x46ca << 10
    	case AMOVVF:
    		return 0x4746 << 10
    	case AMOVVD:
    		return 0x474a << 10
    	case AMOVFW:
    		return 0x46c1 << 10
    	case AMOVDW:
    		return 0x46c2 << 10
    	case AMOVWF:
    		return 0x4744 << 10
    	case AMOVDF:
    		return 0x4646 << 10
    	case AMOVWD:
    		return 0x4748 << 10
    	case AMOVFD:
    		return 0x4649 << 10
    	case AABSF:
    		return 0x4501 << 10
    	case AABSD:
    		return 0x4502 << 10
    	case AMOVF:
    		return 0x4525 << 10
    	case AMOVD:
    		return 0x4526 << 10
    	case ANEGF:
    		return 0x4505 << 10
    	case ANEGD:
    		return 0x4506 << 10
    	case ACMPEQF:
    		return 0x0c1<<20 | 0x4<<15 // FCMP.CEQ.S
    	case ACMPEQD:
    		return 0x0c2<<20 | 0x4<<15 // FCMP.CEQ.D
    	case ACMPGED:
    		return 0x0c2<<20 | 0x7<<15 // FCMP.SLE.D
    	case ACMPGEF:
    		return 0x0c1<<20 | 0x7<<15 // FCMP.SLE.S
    	case ACMPGTD:
    		return 0x0c2<<20 | 0x3<<15 // FCMP.SLT.D
    	case ACMPGTF:
    		return 0x0c1<<20 | 0x3<<15 // FCMP.SLT.S
    
    	case ASQRTF:
    		return 0x4511 << 10
    	case ASQRTD:
    		return 0x4512 << 10
    
    	case ADBAR:
    		return 0x70e4 << 15
    	case ANOOP:
    		// andi r0, r0, 0
    		return 0x03400000
    	}
    
    	if a < 0 {
    		c.ctxt.Diag("bad rrr opcode -%v", -a)
    	} else {
    		c.ctxt.Diag("bad rrr opcode %v", a)
    	}
    	return 0
    }