2

I've read up on the topic of 'type' constraints in e. I saw that it's possible to constrain the types of struct fields, like in this example:

extend DATA packet {
  keep type header is a COP header;
};

This means that whenever I'm dealing with a DATA packet, I don't need to cast header to COP header as the compiler assumes this for me.

I've tried to use a 'type' constraint to tie two different determinant fields. For example, let's say that we have an instruction struct that contains a kind field that encodes the opcode and a format field that determines what arguments the instruction takes:

type instruction_kind : [ ADD, SUB, JMP, BE ] (bits:4);
type format_e : [ RRR, RI, RRI ];

struct instruction {
  const kind : instruction_kind;
  const format : format_e;
};

For example, the RRR format requires the following arguments:

type register_e : [ R0, R1, R2, R3 ] (bits:4);

extend RRR instruction {
  rd : register_e;
  rs1 : register_e;
  rs2 : register_e;
};

An ADD instruction would be of this format. If I only use a simple constraint to tie the ADD with the RRR format, like keep kind == ADD => format == RRR, then to constrain the arguments of an ADD, I'd need to do the following:

extend sys {
  run() is also {
    var instr : instruction;
    gen instr keeping {
      it is a ADD RRR instruction (add) and all of {
        add.rd == R3;
        add.rs1 == R1;
        add.rs2 == R2;
      };
    };
    print instr;
  };
};

Because the arguments are defined under the RRR sub-type, I need to cast to it explicitly. This is rather annoying, because I always need to look up what format each instruction is. It would have been more natural to only have to cast to ADD instruction.

I've tried to use 'type' constraints for this purpose, but without success. If I write the following:

extend ADD instruction {
  keep type me is a RRR instruction;
};

I get the following error message:

*** Error: Type constraints may only be put on fields of this struct

If I write it like this:

extend ADD instruction {
  keep type format == RRR;
};

I get a different error message:

*** Error: Type constraints may only equate a property (enum field) of
the context struct with that of an associated struct

The second error expressly forbids what I'm trying to do, but the first one allows room for interpretation.

I can just extract the arguments into an own struct:

struct instruction_args {
  const format : format_e;
};

extend RRR instruction_args {
  rd : register_e;
  rs1 : register_e;
  rs2 : register_e;
};

and set 'type' constraints as shown in the documentation:

extend instruction {
  when ADD { keep type args is a RRR instruction_args };
};

but now when constraining I always need to prefix every argument with .args:

extend sys {
  run() is also {
    var instr : instruction;
    gen instr keeping {
      it is a ADD instruction (add) and all of {
        add.args.rd == R3;
        add.args.rs1 == R1;
        add.args.rs2 == R2;
      };
    };
    print instr;
  };
};

This isn't a big deal when writing the constraints, but when printing, the contents of args don't get printed. It's an acceptable work-around, but I'm still interested in the initial idea.

Would correlating the when subtypes in the same struct be possible, but I just didn't use the proper syntax?

Tudor Timi
  • 7,453
  • 1
  • 24
  • 53
  • If I understand correctly, what you want to achieve is that whenever you have a variable or expression of declared type `ADD instruction`, it would automatically be treated as `ADD RRR instruction`, allowing to access `RRR`-specific members without explicit casting. But you could achieve the same by explicitly declaring any such variable as `ADD RRR instruction`. In other words, not having any mention of type `ADD instruction` in your code at all, mentioning only `ADD RRR instruction` everywhere. Wouldn't this answer your question? – Yuri Tsoglin Dec 31 '15 at 10:35
  • 1
    @YuriTsoglin That's exactly what I want to avoid. I mean, the example here is a bit out of context. Imagine that I have 100 instructions and 8 formats. I don't want to always have to search for which format fits to which instruction. For example, in the RISC-V ISA there are multiple instruction formats that have the same arguments (rd, rs, imm), but they have different widths, so even knowing what you can constraint for a specific operation isn't enough. The advantage to the sub-type correlation approach would be that I don't need to always look it up. – Tudor Timi Jan 02 '16 at 21:28
  • OK, understood. I hope you do realize that unlike this case, the existing `keep type` comes to solve something that would not be possible otherwise at all. While here you *can* avoid any declaration of the less specific type (when the type is always known to be more specific), `keep type` allows to give a more specific type to a field depending on the field's context struct, which does not apply to the same field in a different context. – Yuri Tsoglin Jan 03 '16 at 09:22

1 Answers1

0

In principle, when it is known apriory that every ADD instruction is actually a ADD RRR instruction, and it is wanted that the compiler will automatically treat every variable or expression of the less specific declared type as more specific (allowing to access the more specific type's members without casting), then you can simply make any such declaration with the more specific type in the first place.

When this is not known apriory, then unfortunately I don't see any better general way to achieve this. A partial workaround could be to define a <type> macro that will convert ADD instruction into ADD RRR instruction, but you would need to define this macro very carefully, because it should catch the exact used syntax (for example, if you want it to catch both short and full when-qualifiers, you would need to define it accordingly). You can try to make the macro define as computed and use the reflection API inside its code to cover various cases.

Yuri Tsoglin
  • 963
  • 4
  • 7
  • I much more prefer the solution with the nested struct. It seems cleaner, since a macro might get too messy to implement. Too bad that it's not possible to correlate sub-types of a single struct (kind of like a degenerate case of what is already available, where the context struct and the associates struct are the same one). – Tudor Timi Jan 04 '16 at 09:57
  • It still seems to me that the "right" solution is to declare more specific types in the first place. If all black dogs are always big, be explicit and declare them as "big black dog" and not as "black dog". – Yuri Tsoglin Jan 04 '16 at 13:53