about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--subx/031check_operands.cc21
1 files changed, 17 insertions, 4 deletions
diff --git a/subx/031check_operands.cc b/subx/031check_operands.cc
index dc8ce67a..3f3b780a 100644
--- a/subx/031check_operands.cc
+++ b/subx/031check_operands.cc
@@ -285,9 +285,10 @@ string maybe_name(const word& op) {
 }
 
 uint32_t compute_expected_operand_bitvector(const line& inst) {
+  set<string> operands_found;
   uint32_t bitvector = 0;
   for (int i = /*skip op*/1;  i < SIZE(inst.words);  ++i) {
-    bitvector = bitvector | expected_bit_for_received_operand(inst.words.at(i));
+    bitvector = bitvector | expected_bit_for_received_operand(inst.words.at(i), operands_found, inst);
     if (trace_contains_errors()) return INVALID_OPERANDS;  // duplicate operand type
   }
   return bitvector;
@@ -310,19 +311,25 @@ int first_operand(const line& inst) {
 
 // Scan the metadata of 'w' and return the expected bit corresponding to any operand type.
 // Also raise an error if metadata contains multiple operand types.
-uint32_t expected_bit_for_received_operand(const word& w) {
+uint32_t expected_bit_for_received_operand(const word& w, set<string>& instruction_operands, const line& inst) {
   uint32_t bv = 0;
   bool found = false;
   for (int i = 0;  i < SIZE(w.metadata);  ++i) {
     string/*copy*/ curr = w.metadata.at(i);
+    string expected_metadata = curr;
     if (curr == "mod" || curr == "rm32" || curr == "r32" || curr == "scale" || curr == "index" || curr == "base")
-      curr = "modrm";
+      expected_metadata = "modrm";
     else if (!contains_key(Operand_type, curr)) continue;  // ignore unrecognized metadata
     if (found) {
       raise << "'" << w.original << "' has conflicting operand types; it should have only one\n" << end();
       return INVALID_OPERANDS;
     }
-    bv = (1 << get(Operand_type, curr));
+    if (instruction_operands.find(curr) != instruction_operands.end()) {
+      raise << "'" << to_string(inst) << "': duplicate " << curr << " operand\n" << end();
+      return INVALID_OPERANDS;
+    }
+    instruction_operands.insert(curr);
+    bv = (1 << get(Operand_type, expected_metadata));
     found = true;
   }
   return bv;
@@ -483,6 +490,12 @@ $error: 0
 89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8
 +error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8' (copy r32 to rm32): unexpected disp8 operand
 
+:(scenario check_duplicate_operand)
+% Hide_errors = true;
+== 0x1
+89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32
++error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32': duplicate r32 operand
+
 :(scenario check_base_operand_not_needed_in_direct_mode)
 == 0x1
 81 0/add/subop 3/mod/indirect 4/rm32/use-sib 1/imm32