Skip to content
Snippets Groups Projects
asm.go 62 KiB
Newer Older
  • Learn to ignore specific revisions
  • }
    
    func buildop(ctxt *obj.Link) {
    	if ctxt.DiagFunc == nil {
    		ctxt.DiagFunc = func(format string, args ...interface{}) {
    			log.Printf(format, args...)
    		}
    	}
    
    	if oprange[AOR&obj.AMask] != nil {
    		// Already initialized; stop now.
    		// This happens in the cmd/asm tests,
    		// each of which re-initializes the arch.
    		return
    	}
    
    	var n int
    
    	for i := 0; i < C_NCLASS; i++ {
    		for n = 0; n < C_NCLASS; n++ {
    			if cmp(n, i) {
    				xcmp[i][n] = true
    			}
    		}
    	}
    	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)
    
    			opset(AFFINTFW, r0)
    			opset(AFFINTFV, r0)
    			opset(AFFINTDW, r0)
    			opset(AFFINTDV, r0)
    			opset(AFTINTWF, r0)
    			opset(AFTINTWD, r0)
    			opset(AFTINTVF, r0)
    			opset(AFTINTVD, r0)
    
    			opset(AFTINTRPWF, r0)
    			opset(AFTINTRPWD, r0)
    			opset(AFTINTRPVF, r0)
    			opset(AFTINTRPVD, r0)
    			opset(AFTINTRMWF, r0)
    			opset(AFTINTRMWD, r0)
    			opset(AFTINTRMVF, r0)
    			opset(AFTINTRMVD, r0)
    			opset(AFTINTRZWF, r0)
    			opset(AFTINTRZWD, r0)
    			opset(AFTINTRZVF, r0)
    			opset(AFTINTRZVD, r0)
    			opset(AFTINTRNEWF, r0)
    			opset(AFTINTRNEWD, r0)
    			opset(AFTINTRNEVF, r0)
    			opset(AFTINTRNEVD, 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)
    
    			opset(AFMINF, r0)
    			opset(AFMIND, r0)
    			opset(AFMAXF, r0)
    			opset(AFMAXD, 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 ABSTRPICKW:
    			opset(ABSTRPICKV, r0)
    			opset(ABSTRINSW, r0)
    			opset(ABSTRINSV, r0)
    
    
    		case ASUB:
    			opset(ASUBU, r0)
    			opset(ANOR, r0)
    
    		case ASUBV:
    			opset(ASUBVU, r0)
    
    		case ASYSCALL:
    			opset(ADBAR, r0)
    
    
    		case ACMPEQF:
    			opset(ACMPGTF, r0)
    			opset(ACMPGTD, r0)
    			opset(ACMPGEF, r0)
    			opset(ACMPGED, r0)
    			opset(ACMPEQD, r0)
    
    		case ABFPT:
    			opset(ABFPF, r0)
    
    		case AMOVW,
    			AMOVD,
    			AMOVF,
    			AMOVV,
    			ARFE,
    			AJAL,
    			AJMP,
    			AMOVWU,
    			ALL,
    			ALLV,
    			ASC,
    			ASCV,
    			ANEGW,
    			ANEGV,
    			AWORD,
    			obj.ANOP,
    			obj.ATEXT,
    			obj.AFUNCDATA,
    
    
    		case AAMSWAPW:
    			for i := range atomicInst {
    				if i == AAMSWAPW {
    					continue
    				}
    				opset(i, r0)
    			}
    
    		}
    	}
    }
    
    // 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
    }
    
    
    func OP_15I(op uint32, i uint32) uint32 {
    	return op | (i&0x7FFF)<<0
    }
    
    
    // i1 -> msb
    // r2 -> rj
    // i3 -> lsb
    // r4 -> rd
    func OP_IRIR(op uint32, i1 uint32, r2 uint32, i3 uint32, r4 uint32) uint32 {
    	return op | (i1 << 16) | (r2&0x1F)<<5 | (i3 << 10) | (r4&0x1F)<<0
    }
    
    
    // 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
    
    		v := c.regoff(&p.From)
    		o1 = OP_15I(c.opi(p.As), uint32(v))
    
    
    	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.
    
    		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
    
    		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 {
    			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))
    		}
    
    
    	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: // bstrpickw $msbw, r1, $lsbw, r2
    		rd, rj := p.To.Reg, p.Reg
    		if rj == obj.REG_NONE {
    			rj = rd
    		}
    		msb, lsb := p.From.Offset, p.GetFrom3().Offset
    
    		// check the range of msb and lsb
    		var b uint32
    		if p.As == ABSTRPICKW || p.As == ABSTRINSW {
    			b = 32
    		} else {
    			b = 64
    		}
    		if lsb < 0 || uint32(lsb) >= b || msb < 0 || uint32(msb) >= b || uint32(lsb) > uint32(msb) {
    			c.ctxt.Diag("illegal bit number\n%v", p)
    		}
    
    		o1 = OP_IRIR(c.opirir(p.As), uint32(msb), uint32(rj), uint32(lsb), uint32(rd))
    
    
    	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 20: // mov Rsrc, (Rbase)(Roff)
    		o1 = OP_RRR(c.oprrr(p.As), uint32(p.To.Index), uint32(p.To.Reg), uint32(p.From.Reg))
    
    	case 21: // mov (Rbase)(Roff), Rdst
    		o1 = OP_RRR(c.oprrr(-p.As), uint32(p.From.Index), uint32(p.From.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)
    		}
    		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(-p.As), uint32(v), uint32(REGTMP), uint32(p.To.Reg))
    
    			o1 = OP_12IRR(c.opirr(-p.As), 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)
    		}
    		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(p.As), uint32(v), uint32(REGTMP), uint32(p.From.Reg))
    
    			o1 = OP_12IRR(c.opirr(p.As), uint32(v), uint32(r), uint32(p.From.Reg))
    
    	case 30: // mov gr/fr/fcc/fcsr, fr/fcc/fcsr/gr
    		a := c.specailFpMovInst(p.As, oclass(&p.From), oclass(&p.To))
    
    		o1 = OP_RR(a, uint32(p.From.Reg), uint32(p.To.Reg))
    
    	case 34: // mov $con,fr
    		v := c.regoff(&p.From)
    		a := AADDU
    
    		a2 := c.specailFpMovInst(p.As, C_REG, oclass(&p.To))
    
    		o1 = OP_12IRR(c.opirr(a), uint32(v), uint32(0), uint32(REGTMP))
    
    		o2 = OP_RR(a2, uint32(REGTMP), uint32(p.To.Reg))
    
    
    	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 49:
    		if p.As == ANOOP {
    			// andi r0, r0, 0
    			o1 = OP_12IRR(c.opirr(AAND), 0, 0, 0)
    		} else {
    			// undef
    			o1 = OP_15I(c.opi(ABREAK), 0)
    		}
    
    	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_LOONG64_ADDR_HI
    
    
    		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_LOONG64_ADDR_LO
    
    	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_LOONG64_ADDR_HI
    
    		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_LOONG64_ADDR_LO
    
    		// 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_LOONG64_ADDR_HI
    
    		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_LOONG64_ADDR_LO
    
    
    	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_LOONG64_TLS_LE_HI
    
    		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_LOONG64_TLS_LE_LO
    
    		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_LOONG64_TLS_LE_HI
    
    		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_LOONG64_TLS_LE_LO
    
    		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 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_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_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 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
    
    
    	case 66: // am* From, To, RegTo2 ==> am* RegTo2, From, To
    		rk := p.From.Reg
    		rj := p.To.Reg
    		rd := p.RegTo2
    
    		// See section 2.2.7.1 of https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html
    		// for the register usage constraints.
    		if rd == rj || rd == rk {
    			c.ctxt.Diag("illegal register combination: %v\n", p)
    		}
    		o1 = OP_RRR(atomicInst[p.As], uint32(rk), uint32(rj), uint32(rd))
    
    	}
    
    	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 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 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 AFMINF:
    		return 0x215 << 15 // fmin.s
    	case AFMIND:
    		return 0x216 << 15 // fmin.d
    	case AFMAXF:
    		return 0x211 << 15 // fmax.s
    	case AFMAXD:
    		return 0x212 << 15 // fmax.d
    
    	case AFCOPYSGF:
    		return 0x225 << 15 // fcopysign.s
    	case AFCOPYSGD:
    		return 0x226 << 15 // fcopysign.d
    
    	case -AMOVB:
    		return 0x07000 << 15 // ldx.b
    	case -AMOVH:
    		return 0x07008 << 15 // ldx.h
    	case -AMOVW:
    		return 0x07010 << 15 // ldx.w
    	case -AMOVV:
    		return 0x07018 << 15 // ldx.d
    	case -AMOVBU:
    		return 0x07040 << 15 // ldx.bu
    	case -AMOVHU:
    		return 0x07048 << 15 // ldx.hu
    	case -AMOVWU:
    		return 0x07050 << 15 // ldx.wu
    	case AMOVB: