Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AArch64] SME instructions with indexed operands do not have correct disassembly information #2285

Open
FinnWilkinson opened this issue Mar 7, 2024 · 16 comments · May be fixed by #2298
Open
Labels
Milestone

Comments

@FinnWilkinson
Copy link
Contributor

FinnWilkinson commented Mar 7, 2024

Working on the latest next branch, disassembling a range of SME instructions yields incorrect disassembly information, often with additional memory operands being defined.

It seems to be an issue with the pattern matching of [...]. A similar issue occurred when implementing PR #1907 and was fixed in PR #1925. I'm not sure how different the backend is now after the auto-sync update, but perhaps a similar fix can be implemented from this?

Listed below are some examples:

Issue - Operand 2 p3.s's index is identified as an additional memory operand.

cstool -d aarch64 67447125
 0  67 44 71 25  psel	p7, p1, p3.s[w13, 1]
	ID: 735 (psel)
	op_count: 4
		operands[0].type: REG = p7
		operands[0].access: WRITE
		operands[1].type: REG = p1
		operands[1].access: READ
		operands[2].type: REG = p3
		operands[2].access: READ
			Vector Arrangement Specifier: 0x20
		operands[3].type: MEM
			operands[3].mem.base: REG = w13
			operands[3].mem.disp: 0x1
	Registers read: p1 p3 w13
	Registers modified: p7
	Groups: HasSVE2p1_or_HasSME

Issue - Operand p4 is mistaken for a memory operand and x10 as its register offset. Also, operands[0] should not have an access specifier, and operands[2] should not have a vector index

cstool -d aarch64 4131a2e0
 0  41 31 a2 e0  st1w	{za0h.s[w13, 1]}, p4, [x10, x2, lsl #2]
	ID: 1047 (st1w)
	op_count: 3
		operands[0].type: SME_MATRIX
		operands[0].sme.type: 2
		operands[0].sme.tile: za0.s
		operands[0].sme.slice_reg: w13
		operands[0].sme.slice_offset: 1
		operands[0].sme.is_vertical: false
		operands[0].access: WRITE
			Vector Arrangement Specifier: 0x20
			Vector Index: 0
		operands[1].type: MEM
			operands[1].mem.base: REG = p4
			operands[1].mem.index: REG = x10
		operands[1].access: WRITE
			Shift: type = 1, value = 2
			Vector Index: 0
		operands[2].type: MEM
			operands[2].mem.base: REG = x2
		operands[2].access: WRITE
			Vector Index: 0
	Registers read: p4 x10 x2
	Groups: HasSME

Issue - Same as above.

cstool -d aarch64 c0089fe0
 0  c0 08 9f e0  ld1w	{za0h.s[w12, 0]}, p2/z, [x6]
	ID: 434 (ld1w)
	Is alias: 1354 (ld1w) with ALIAS operand set
	op_count: 2
		operands[0].type: SME_MATRIX
		operands[0].sme.type: 2
		operands[0].sme.tile: za0.s
		operands[0].sme.slice_reg: w12
		operands[0].sme.slice_offset: 0
		operands[0].sme.is_vertical: false
		operands[0].access: READ
			Vector Arrangement Specifier: 0x20
			Vector Index: 0
		operands[1].type: MEM
			operands[1].mem.base: REG = p2
			operands[1].mem.index: REG = x6
		operands[1].access: READ
			Vector Index: 0
	Registers read: p2 x6
	Groups: HasSME
@FinnWilkinson
Copy link
Contributor Author

FinnWilkinson commented Mar 7, 2024

Looking into this a bit further, it seems to originate from the AArch64GenCSMappingInsnOp.inc file where all SME based instructions with an index have CS_OP_MEM.

In capstone's LLVM, llvm/utils/TableGen/PrinterCapstone.cpp the function getCSOperandType contains

if (TargetName.equals("AArch64") && OperandType != "CS_OP_MEM") {
    // The definitions of AArch64 are so broken, when it comes to memory
    // operands, that we just search for the op name enclosed in [].
    if (Regex("\\[.*\\$" + OpName.str() + ".*]").match(CGI->AsmString))
      return OperandType += " | CS_OP_MEM";
  }

Which would explain the above issues with LD1W and ST1W whereby p2 is seen as a memory operand (The open bracket [ is part of za0h's index, the close bracket ] is part of the actual memory operand

I think it also explains the issue with PSEL as all predicate registers are recognised correctly but p3's index is seen as a memory operand rather than its index.

Looking at other SME instructions in AArch64GenCSMappingInsnOp.inc, instruction opcode AArch64_ADD_VG2_M2Z2Z_D could potentially present a similar issue to PSEL where ZA's index is seen as a memory operand rather than an index --- again this could be explained by the above regex

{ /* AArch64_ADD_VG2_M2Z2Z_D (1265) - AArch64_INS_ADD - add	$ZAd[$Rv, $imm3, vgx2], $Zn, $Zm */
{
  { CS_OP_REG, CS_AC_WRITE, { CS_DATA_TYPE_Untyped, CS_DATA_TYPE_LAST } }, /* ZAd */
  { CS_OP_REG, CS_AC_READ, { CS_DATA_TYPE_Untyped, CS_DATA_TYPE_LAST } }, /* _ZAd */
  { CS_OP_REG | CS_OP_MEM, CS_AC_INVALID, { CS_DATA_TYPE_i32, CS_DATA_TYPE_LAST } }, /* Rv */
  { CS_OP_IMM | CS_OP_MEM, CS_AC_INVALID, { CS_DATA_TYPE_i32, CS_DATA_TYPE_LAST } }, /* imm3 */
  { CS_OP_REG, CS_AC_READ, { CS_DATA_TYPE_Untyped, CS_DATA_TYPE_LAST } }, /* Zn */
  { CS_OP_REG, CS_AC_READ, { CS_DATA_TYPE_Untyped, CS_DATA_TYPE_LAST } }, /* Zm */
  { 0 }
}},

@FinnWilkinson
Copy link
Contributor Author

FinnWilkinson commented Mar 8, 2024

I'm not 100% sure on how to implement a fix for this, but for the regex above; all AArch64 memory operands will be preceeded by , (i.e. , [x6]) and so the regex needs to accout for this. If [...] is not preceeded by a comma and a space then we know it to be an index.

Additionally, said regex should check that there is no ] before the OpName.str() to bulletproof it from issues seen with PSEL. It may also require another aarch64_op_type and/or member of the union in cs_aarch64_op as PSEL does not nicely fit the criteria for aarch64_op_sme but still requires a register to have an index with a base register and immidiate offset.

As far as I can tell, other AArch64 instructions do not seem to be affected, only SME/SME2.

@Rot127
Copy link
Collaborator

Rot127 commented Mar 9, 2024

Thanks a lot for the detailed issue! Currently I am updating to LLVM 18 and will add your fix after it.

Couldn't find the time yet to address the other issues in #2196 unfortunately. But will address them and other bugs after the LLVM 18 update. Since it adds new AArch64 instructions anyways.

@FinnWilkinson
Copy link
Contributor Author

Great, thank you for the update!

@Rot127 Rot127 added this to the v6 milestone Mar 19, 2024
@Rot127 Rot127 linked a pull request Mar 25, 2024 that will close this issue
7 tasks
@Rot127
Copy link
Collaborator

Rot127 commented May 16, 2024

@FinnWilkinson I am currently at this. Thanks for spotting the faulty regex. It is fixed now.

Regarding the representation of SME operands.
What do you think about having two types of SME operands. Once an indexed matrix (which is the struct currently called aarch64_op_sme) and an "indexed" predicate reg op?

Something like:

typedef struct {
  aarch64_sme_op_type type; ///< AArch64_SME_OP_TILE, AArch64_SME_OP_TILE_VEC
  aarch64_reg tile; ///< Matrix tile register
  aarch64_reg slice_reg; ///< slice index reg
  // ...
} aarch64_op_sme_matrix;

typedef struct {
  // Some type field
  aarch64_reg pred_reg;
  aarch64_reg vec_select;
  int32_t index; ///< Potentially also a union of other ops which can be used here.
} aarch64_op_sme_pred;

typedef struct {
  // Some type field
  union {
    aarch64_op_sme_matrix matrix;
    aarch64_op_sme_pred pred;
  }
} aarch64_sme_op;

I am a little hesitant to add more operands. But the SME ops don't really fit in the previous ones. So I'd really appreciate your comment on this, since you seem to work with this AArch64 extension a lot.

As far as I can tell, other AArch64 instructions do not seem to be affected, only SME/SME2.

Yeah, SME/SME2 is really not that well defined in LLVM, unfortunately. So they need all kind of hacky solutions.

@Rot127
Copy link
Collaborator

Rot127 commented May 20, 2024

This is now the resulting output:

cstool -d aarch64 c0089fe04131a2e067447125
 0  c0 08 9f e0  ld1w	{za0h.s[w12, 0]}, p2/z, [x6]
	ID: 474 (ld1w)
	Is alias: 1466 (ld1w) with ALIAS operand set
	op_count: 3
		operands[0].type: SME_MATRIX
		operands[0].sme.type: 2
		operands[0].sme.mx.tile: za0.s
		operands[0].sme.mx.slice_reg: w12
		operands[0].sme.mx.slice_offset: 0
		operands[0].sme.mx.is_vertical: false
		operands[0].access: WRITE
			Vector Arrangement Specifier: 0x20
		operands[1].type: SME_PRED
		operands[1].sme.pred.reg: p2
		operands[1].access: READ
		operands[2].type: MEM
			operands[2].mem.base: REG = x6
		operands[2].access: READ
	Registers read: za0.s w12 p2 x6
	Groups: HasSME 

 4  41 31 a2 e0  st1w	{za0h.s[w13, 1]}, p4, [x10, x2, lsl #2]
	ID: 1099 (st1w)
	op_count: 3
		operands[0].type: SME_MATRIX
		operands[0].sme.type: 2
		operands[0].sme.mx.tile: za0.s
		operands[0].sme.mx.slice_reg: w13
		operands[0].sme.mx.slice_offset: 1
		operands[0].sme.mx.is_vertical: false
		operands[0].access: READ
			Vector Arrangement Specifier: 0x20
		operands[1].type: SME_PRED
		operands[1].sme.pred.reg: p4
		operands[1].access: READ
		operands[2].type: MEM
			operands[2].mem.base: REG = x10
			operands[2].mem.index: REG = x2
		operands[2].access: WRITE
			Shift: type = 1, value = 2
	Registers read: za0.s w13 p4 x10 x2
	Groups: HasSME 

 8  67 44 71 25  psel	p7, p1, p3.s[w13, 1]
	ID: 785 (psel)
	op_count: 3
		operands[0].type: SME_PRED
		operands[0].sme.pred.reg: p7
		operands[0].access: WRITE
		operands[1].type: SME_PRED
		operands[1].sme.pred.reg: p1
		operands[1].access: READ
		operands[2].type: SME_PRED
		operands[2].sme.pred.reg: p3
		operands[2].sme.pred.vec_select: w13
		operands[2].sme.pred.index: 1
		operands[2].access: READ
			Vector Arrangement Specifier: 0x20
	Registers read: p7 p1 p3 w13
	Groups: HasSVE2p1_or_HasSME 

@FinnWilkinson
Copy link
Contributor Author

Hi @Rot127 , sorry for the delayed reply I've only just seen the newest comments. This generallt all looks good to me, but I have some concerns about predicates in HasSME instructions. I assume only predicates in instructions with the group HasSME are of type sme.pred.reg? Even so, I think it is slightly confusing. For example, in psel there is no reference to an SME operand, yet all predicates are now sme.pred.reg even when the predicate is not indexed. A possible solution could be to add a variable to cs_aarch_op called index which any operand can have:

typedef struct aarch64_op_index {
  aarch64_reg base;
  int32_t imm_offset;
} aarch64_op_index;

This could also be used by NEON, SVE, and predicate registers and remove the need for vector_index. I'm sure this change in non-trivial though (and adds to current logic which isn't ideal), but could tidy things up a bit?

Additionally, just to ensure functional correctness, what do the following instructions get disassembled as?

  • fmopa za2.s, p0/m, p2/m, z3.s, z4.s ==== 40208180
  • smstart ==== 7f4703d5
  • zero {za0.s, za2.s} ==== 550008c0
    • (za0.h is a valid alias for za0.s and za2.s)

@Rot127
Copy link
Collaborator

Rot127 commented May 21, 2024

A possible solution could be to add a variable to cs_aarch_op called index which any operand can have:

The thing is that these indices differ quite a lot (the matrix indices can have immediate ranges. And additionally the terminology differs between the members of the indices structs (slice, vector select etc.)). Would you be ok with:

  • Removing the SME operand
  • Adding an Index struct as suggested with: index_type (None, VectorPredicate, Matrix or something) + a union with the different indices structs.

I'd like to have the terminology of the struct fields relatively close to the ISA, if possible
Hence not just one index struct, but multiple.

Generally though I would follow your advice. I only interact with AArch64 in the Capstone context. And because you seem to use it heavily in practice, I would follow your design in the end.

I'm sure this change in non-trivial though (and adds to current logic which isn't ideal), but could tidy things up a bit?

As long as we don't change it after the v6 release, it's ok.
v6 will be huge, and although it is annoying to have these API changes here in next, it is important that it is done right before v6. And next is still a development branch in the end.

Additionally, just to ensure functional correctness, what do the following instructions get disassembled as?

Thanks for the test cases! As it looks like the alias versions are still miss some operands.
Please let me know any other errors.

If you have more test cases, please let me know. Those operands are pretty complex.

Details

With ALIAS operand set

cstool -d aarch64 402081807f4703d5550008c0
 0  40 20 81 80  fmopa	za0.s, p0/m, p1/m, z2.s, z1.s
	ID: 417 (fmopa)
	op_count: 5
		operands[0].type: SME_MATRIX
		operands[0].sme.type: 1
		operands[0].sme.mx.tile: za0.s
		operands[0].access: READ | WRITE
			Vector Arrangement Specifier: 0x20
		operands[1].type: SME_PRED
		operands[1].sme.pred.reg: p0
		operands[1].access: READ
		operands[2].type: SME_PRED
		operands[2].sme.pred.reg: p1
		operands[2].access: READ
		operands[3].type: REG = z2
		operands[3].access: READ
			Vector Arrangement Specifier: 0x20
		operands[4].type: REG = z1
		operands[4].access: READ
			Vector Arrangement Specifier: 0x20
	Write-back: True
	Registers read: za0.s p0 p1 z2 z1
	Groups: HasSME 

 4  7f 47 03 d5  smstart	
	ID: 734 (msr)
	Is alias: 1468 (smstart) with ALIAS operand set
	Update-flags: True
	Registers modified: nzcv
	Groups: privilege 

 8  55 00 08 c0  zero	{za0.h}
	ID: 1384 (zero)
	Is alias: 1470 (zero) with ALIAS operand set
	Groups: HasSME 

With REAL operand set

04:16 $ cstool -r -d aarch64 402081807f4703d5550008c0
 0  40 20 81 80  fmopa	za0.s, p0/m, p1/m, z2.s, z1.s
	ID: 417 (fmopa)
	op_count: 5
		operands[0].type: SME_MATRIX
		operands[0].sme.type: 1
		operands[0].sme.mx.tile: za0.s
		operands[0].access: READ | WRITE
			Vector Arrangement Specifier: 0x20
		operands[1].type: SME_PRED
		operands[1].sme.pred.reg: p0
		operands[1].access: READ
		operands[2].type: SME_PRED
		operands[2].sme.pred.reg: p1
		operands[2].access: READ
		operands[3].type: REG = z2
		operands[3].access: READ
			Vector Arrangement Specifier: 0x20
		operands[4].type: REG = z1
		operands[4].access: READ
			Vector Arrangement Specifier: 0x20
	Write-back: True
	Registers read: za0.s p0 p1 z2 z1
	Groups: HasSME 

 4  7f 47 03 d5  smstart	
	ID: 734 (msr)
	Is alias: 1468 (smstart) with REAL operand set
	op_count: 2
		operands[0].type: SYS ALIAS:
			operands[0].svcr: BIT = SM & ZA
		operands[1].type: IMM = 0x1
		operands[1].access: READ
	Update-flags: True
	Registers modified: nzcv
	Groups: privilege 

 8  55 00 08 c0  zero	{za0.h}
	ID: 1384 (zero)
	Is alias: 1470 (zero) with REAL operand set
	op_count: 1
		operands[0].type: SME_MATRIX
		operands[0].sme.type: 1
		operands[0].sme.mx.tile: za6.d
		operands[0].access: READ
			Vector Arrangement Specifier: 0x40
	Registers read: za6.d
	Groups: HasSME 

@FinnWilkinson
Copy link
Contributor Author

I think the current implementation of SME operands (non predicates) works well, so I'd be opposed to changing from this. How about having a new aarch64_op_pred type for all predicates (not just HasSME)? This leaves vector and SME indexing as is, and would make indexed predicates a tad easier IMO without too much overhead for normal, non-indexed, predicate registers.

For the test case outputs:

  • If smstart ALIAS version printed the same as REAL then that would be helpful
  • zero ALIAS is missing the register, and REAL looks to select the incorrect register (this isn't a valid alias as far as I know)

I'll compile some more test cases now and put them here, but I need to formulate the hex first.

@Rot127
Copy link
Collaborator

Rot127 commented May 21, 2024

I'll compile some more test cases now and put them here, but I need to formulate the hex first.

This would be great. Thank you!

How about having a new aarch64_op_pred type for all predicates (not just HasSME)?

Ok, sounds good to me. To summarize:

  • Add new operand type aarch64_op_pred and aarch64_op_index.
    typedef struct aarch64_op_index {
      aarch64_reg base;
      int32_t imm_offset;
    } aarch64_op_index;
    
    typedef struct aarch64_op_pred {
      aarch64_reg pred;
      aarch64_op_index index;
    } aarch64_op_pred;
  • Operands in AArch64 update to LLVM 18 #2298 which are sme.pred, get converted to the new aarch64_op_pred format.
  • Leave sme untouched (meaning: in AArch64 update to LLVM 18 #2298 change it back to the previous sme operand, instead of sme.mx).

@FinnWilkinson
Copy link
Contributor Author

FinnWilkinson commented May 21, 2024

Will aarch64_op_index be used for anything else? If not couldn't we just do

typedef struct aarch64_op_pred {
  aarch64_reg pred;
  aarch64_reg vec_select;
  int32_t im_offset;
} aarch64_op_pred;

?

And for sme instructions yeah, just removing the .mx part to yield (for example)

                ...
		operands[0].type: SME
		operands[0].sme.type: 2
		operands[0].sme.tile: za0.s
		operands[0].sme.slice_reg: w12
		operands[0].sme.slice_offset: 0
		operands[0].sme.is_vertical: false
                ...

seems good to me.

@Rot127
Copy link
Collaborator

Rot127 commented May 21, 2024

Will aarch64_op_index be used for anything else? If not couldn't we just do

Ah, well. Yes. Let's just do it as you said.
I wasn't paying attention and thought about the SME matrix as well. But let's just hope ARM will not introduce more extensions with this index pattern.

@FinnWilkinson
Copy link
Contributor Author

FinnWilkinson commented May 21, 2024

But let's just hope ARM will not introduce more extensions with this index pattern.

Yes, lets!

Here are some more complex assembly tests that would be good to validate as working. At the moment this is about as complex as SVE and SME gets (hex may be the wrong way round):

  • sdot za.s[w11, 2, vgx4], {z0.h-z3.h}, z5.h[2] ==== c155f802
  • movaz {z4.d-z7.d}, za.d[w8, 5, vgx4] ==== c0060ea4
  • luti2 {z0.s-z3.s}, zt0, z4[1] ==== c08da080
  • fmla za.h[w9, 0, vgx4], {z8.h-z11.h}, z0.h[0] ==== c110b100
  • fmlal za.s[w10, 2:3, vgx4], {z0.h-z3.h}, z11.h[1] ==== c19bd005

@Rot127
Copy link
Collaborator

Rot127 commented May 22, 2024

Change is done. Also fixed, that the predicate regs were not added to the register written list.

Details
> cstool -d aarch64 c0089fe04131a2e067447125

 0  c0 08 9f e0  ld1w	{za0h.s[w12, 0]}, p2/z, [x6]
	ID: 474 (ld1w)
	Is alias: 1466 (ld1w) with ALIAS operand set
	op_count: 3
		operands[0].type: SME_MATRIX
		operands[0].sme.type: 2
		operands[0].sme.tile: za0.s
		operands[0].sme.slice_reg: w12
		operands[0].sme.slice_offset: 0
		operands[0].sme.is_vertical: false
		operands[0].access: WRITE
			Vector Arrangement Specifier: 0x20
		operands[1].type: PREDICATE
		operands[1].pred.reg: p2
		operands[1].access: READ
		operands[2].type: MEM
			operands[2].mem.base: REG = x6
		operands[2].access: READ
	Registers read: za0.s w12 p2 x6
	Groups: HasSME 

 4  41 31 a2 e0  st1w	{za0h.s[w13, 1]}, p4, [x10, x2, lsl #2]
	ID: 1099 (st1w)
	op_count: 3
		operands[0].type: SME_MATRIX
		operands[0].sme.type: 2
		operands[0].sme.tile: za0.s
		operands[0].sme.slice_reg: w13
		operands[0].sme.slice_offset: 1
		operands[0].sme.is_vertical: false
		operands[0].access: READ
			Vector Arrangement Specifier: 0x20
		operands[1].type: PREDICATE
		operands[1].pred.reg: p4
		operands[1].access: READ
		operands[2].type: MEM
			operands[2].mem.base: REG = x10
			operands[2].mem.index: REG = x2
		operands[2].access: WRITE
			Shift: type = 1, value = 2
	Registers read: za0.s w13 p4 x10 x2
	Groups: HasSME 

 8  67 44 71 25  psel	p7, p1, p3.s[w13, 1]
	ID: 785 (psel)
	op_count: 3
		operands[0].type: PREDICATE
		operands[0].pred.reg: p7
		operands[0].access: WRITE
		operands[1].type: PREDICATE
		operands[1].pred.reg: p1
		operands[1].access: READ
		operands[2].type: PREDICATE
		operands[2].pred.reg: p3
		operands[2].pred.vec_select: w13
		operands[2].pred.imm_index: 1
		operands[2].access: READ
			Vector Arrangement Specifier: 0x20
	Registers read: p1 p3 w13
	Registers modified: p7
	Groups: HasSVE2p1_or_HasSME 

If smstart ALIAS version printed the same as REAL then that would be helpful

Sorry, late night working. Got confused with my own implementation.
This behavior (alias not printing the operands) is on purpose. cstool is supposed to print only the details which
are part of the asm text (this is forced by the design of Capstone).

The real operand set can always be accessed, either by adding the -r flag to cstool or by enabling the
CS_OPT_DETAIL_REAL option.

The reason is: to retrieve both detail sets, an instruction must be disassembled twice.
And this is a choice only the user should make. So we don't double the runtime by default.

zero ALIAS is missing the register, and REAL looks to select the incorrect register (this isn't a valid alias as far as I know)

Fix this later this week.

The new test cases

Output is below. Note the register lists. I didn't add a new operand type for those.
If you say it would be a huge improvement to have them in a separated struct, we can talk about the implementation.
Otherwise, I'd like to let the user deduct the registers in between the first and last register.
Just to keep it somewhat simple.

Same for the vgx4 indicators. They are not stored in the details, because it can be deducted by checking the list members.

Details
 > cstool -d aarch64be c155f802c0060ea4c08da080c110b100c19bd005

 0  c1 55 f8 02  sdot	za.s[w11, 2, vgx4], { z0.h - z3.h }, z5.h[2]
	ID: 922 (sdot)
	op_count: 4
		operands[0].type: SME_MATRIX
		operands[0].sme.type: 2
		operands[0].sme.tile: za
		operands[0].sme.slice_reg: w11
		operands[0].sme.slice_offset: 2
		operands[0].sme.is_vertical: false
		operands[0].access: READ | WRITE
			Vector Arrangement Specifier: 0x20
		operands[1].type: REG = z0
		operands[1].is_list_member: true
		operands[1].access: READ
			Vector Arrangement Specifier: 0x10
		operands[2].type: REG = z3
		operands[2].is_list_member: true
		operands[2].access: READ
			Vector Arrangement Specifier: 0x10
		operands[3].type: REG = z5
		operands[3].access: READ
			Vector Arrangement Specifier: 0x10
			Vector Index: 2
	Write-back: True
	Registers read: za w11 z0 z3 z5
	Groups: HasSME2 

 4  c0 06 0e a4  movaz	{ z4.d - z7.d }, za.d[w8, 5, vgx4]
	ID: 724 (movaz)
	op_count: 3
		operands[0].type: REG = z4
		operands[0].is_list_member: true
		operands[0].access: WRITE
			Vector Arrangement Specifier: 0x40
		operands[1].type: REG = z7
		operands[1].is_list_member: true
		operands[1].access: WRITE
			Vector Arrangement Specifier: 0x40
		operands[2].type: SME_MATRIX
		operands[2].sme.type: 2
		operands[2].sme.tile: za
		operands[2].sme.slice_reg: w8
		operands[2].sme.slice_offset: 5
		operands[2].sme.is_vertical: false
		operands[2].access: READ | WRITE
			Vector Arrangement Specifier: 0x40
	Write-back: True
	Registers read: za w8
	Registers modified: z4 z7
	Groups: HasSME2p1 

 8  c0 8d a0 80  luti2	{ z0.s - z3.s }, zt0, z4[1]
	ID: 710 (luti2)
	op_count: 4
		operands[0].type: REG = z0
		operands[0].is_list_member: true
		operands[0].access: WRITE
			Vector Arrangement Specifier: 0x20
		operands[1].type: REG = z3
		operands[1].is_list_member: true
		operands[1].access: WRITE
			Vector Arrangement Specifier: 0x20
		operands[2].type: SME_MATRIX
		operands[2].sme.type: 1
		operands[2].sme.tile: zt0
		operands[2].access: READ
		operands[3].type: REG = z4
		operands[3].access: READ
			Vector Index: 1
	Registers read: zt0 z4
	Registers modified: z0 z3
	Groups: HasSME2 

 c  c1 10 b1 00  fmla	za.h[w9, 0, vgx4], { z8.h - z11.h }, z0.h[0]
	ID: 410 (fmla)
	op_count: 4
		operands[0].type: SME_MATRIX
		operands[0].sme.type: 2
		operands[0].sme.tile: za
		operands[0].sme.slice_reg: w9
		operands[0].sme.slice_offset: 0
		operands[0].sme.is_vertical: false
		operands[0].access: READ | WRITE
			Vector Arrangement Specifier: 0x10
		operands[1].type: REG = z8
		operands[1].is_list_member: true
		operands[1].access: READ
			Vector Arrangement Specifier: 0x10
		operands[2].type: REG = z11
		operands[2].is_list_member: true
		operands[2].access: READ
			Vector Arrangement Specifier: 0x10
		operands[3].type: REG = z0
		operands[3].access: READ
			Vector Arrangement Specifier: 0x10
			Vector Index: 0
	Write-back: True
	Registers read: za w9 z8 z11 z0
	Groups: HasSME2p1 HasSMEF16F16 

10  c1 9b d0 05  fmlal	za.s[w10, 2:3, vgx4], { z0.h - z3.h }, z11.h[1]
	ID: 409 (fmlal)
	op_count: 4
		operands[0].type: SME_MATRIX
		operands[0].sme.type: 2
		operands[0].sme.tile: za
		operands[0].sme.slice_reg: w10
		operands[0].sme.slice_offset: 2:3
		operands[0].sme.is_vertical: false
		operands[0].access: READ | WRITE
			Vector Arrangement Specifier: 0x20
		operands[1].type: REG = z0
		operands[1].is_list_member: true
		operands[1].access: READ
			Vector Arrangement Specifier: 0x10
		operands[2].type: REG = z3
		operands[2].is_list_member: true
		operands[2].access: READ
			Vector Arrangement Specifier: 0x10
		operands[3].type: REG = z11
		operands[3].access: READ
			Vector Arrangement Specifier: 0x10
			Vector Index: 1
	Write-back: True
	Registers read: za w10 z0 z3 z11
	Groups: HasSME2 

@FinnWilkinson
Copy link
Contributor Author

FinnWilkinson commented May 22, 2024

Change is done. Also fixed, that the predicate regs were not added to the register written list.

I think this looks great now. Thanks!

Sorry, late night working. Got confused with my own implementation.
This behavior (alias not printing the operands) is on purpose. cstool is supposed to print only the details which
are part of the asm text (this is forced by the design of Capstone).

Understood!

Note the register lists. I didn't add a new operand type for those.

Given you can have strided vector register lists as well as consecutive (although I'm struggling to find an example currently), I think it would be beneficial to print them individually for both clarity and for using Capstone as a tool inside other projects. Rather than a new structure though, could we not print them as seperate operands?
i,.e. luti2 { z0.s - z3.s }, zt0, z4[1] would be:

8  c0 8d a0 80  luti2	{ z0.s - z3.s }, zt0, z4[1]
	ID: 710 (luti2)
	op_count: 6
		operands[0].type: REG = z0
		operands[0].is_list_member: true
		operands[0].access: WRITE
			Vector Arrangement Specifier: 0x20
		operands[1].type: REG = z1
		operands[1].is_list_member: true
		operands[1].access: WRITE
			Vector Arrangement Specifier: 0x20
		operands[2].type: REG = z2
		operands[2].is_list_member: true
		operands[2].access: WRITE
			Vector Arrangement Specifier: 0x20
		operands[3].type: REG = z3
		operands[3].is_list_member: true
		operands[3].access: WRITE
			Vector Arrangement Specifier: 0x20
		operands[4].type: SME_MATRIX
		operands[4].sme.type: 1
		operands[4].sme.tile: zt0
		operands[4].access: READ
		operands[5].type: REG = z4
		operands[6].access: READ
			Vector Index: 1
	Registers read: zt0 z4
	Registers modified: z0 z1 z2 z3
	Groups: HasSME2 

The SME2 spec also states that although { z0.s - z3.s } is preferred disassembly, there must also be support for { z0.s, z1.s, z2.s, z3.s }. We could change to this other disassembly format to make list operands clearer, but I prefer the simpler format (i.e. keep it as it is now).

Same for the vgx4 indicators.

I agree with not printing vgx4 etc, as the vg syntax is optional disassembly as per the spec and doesn't really add much to the understanding. Having it printed in the disassembly is enough IMO.

As an aside, should zt0 be classed as a normal register rahter than SME_MATRIX? Although only accessible in Streaming SVE mode, it is a seperate register to ZA. And from what I can tell from the spec, it is not indexable (yet...). If you think theres a good reason to keep zt0 as is though then I'm still ok with how its currently displayed.

@FinnWilkinson
Copy link
Contributor Author

FinnWilkinson commented May 22, 2024

I've also just noticed that SMSTART currently has Registers modified: nzcv which is false (as far as I can tell from the spec). Only PSTATE is updated. Similar is true for SMSTOP.

https://developer.arm.com/documentation/ddi0602/2024-03/Base-Instructions/SMSTART--Enables-access-to-Streaming-SVE-mode-and-SME-architectural-state--an-alias-of-MSR--immediate--

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Todo
Development

Successfully merging a pull request may close this issue.

2 participants