在汇编中实现正则表达式“[ab][^r]+r]”的匹配器

2023-12-07

我的汇编代码需要帮助。我需要使用编写代码来找到适合我的正则表达式的范围。

我的正则表达式:[ab][^r]+r,所以首先我寻找是否有“a”或“b”并跳转到“开始”部分。现在我有一个问题如何仅保存这封信的第一次出现。 程序应显示:5, 10- 这意味着,匹配的字符串从第 5 个位置开始,长度为 10。我想保存的程序结果'ecx' and 'edx'注册表(或者我可以简化它吗?)

我将不胜感激所有的建议和帮助:)

这是一个代码:

#include <stdio.h>

int main(void) 
{
  char *s = "fqr  b qabxx  xryc pqr"; // string length= 22, first occurence: 5 position, second one: 9
  int x, y;

asm volatile (
".intel_syntax noprefix;"
"mov eax, %2;"

"xor ecx, ecx;"
"xor edx, edx;"

"lea ebx, [eax];" 

"loop:"
  "mov al, [ebx];" 
  "or al, al;" 
  "jz Finish;"
  "inc edx;"

  "cmp al, 'a';" 
  "je Start;"

  "cmp al, 'b';"
  "je Start;"

  "jmp JumpNext;"

 "Start:" 
   "mov ecx, edx;"
   "jmp loop;"

  "JumpNext:"
    "inc ebx;"
    "jmp loop;"

 "Finish:"
  "mov %0, ecx;"
  "mov %1, edx;"

".att_syntax prefix;"
:"=r" (x), "=r" (y)
:"r" (s)
:"eax", "ebx", "ecx", "edx"
);

printf("%d, %d \n", x, y);
return 0; 
}

编辑:这是完成的代码:

 #include <stdio.h>


int main(void)
{
  char *s = "fqr  b qabxx xryc pqr"; 
  int x, y;

  asm volatile (
    ".intel_syntax noprefix;"

    "xor ecx, ecx;" // First occurrence of letter 'a' or 'b'
    "mov edx, 1;" // Matching string length

    "lea ebx, [%2];"

    "loop:"
      "mov al, [ebx];"
      "cmp al, 0;"
      "jz ThereisNoChars;" 

      "cmp al, 'a';" 
      "je FoundAorB;"

      "cmp al, 'b';" 
      "je FoundAorB;"

      "inc ecx;"
      "inc ebx;"
      "jmp loop;"

    "FoundAorB:"
      "inc ebx;"
      "inc edx;" 

      "mov al, [ebx];"
      "or al, al;"
      "jz ThereisNoChars;"

      "cmp al, 'r';"
      "je isRafterAorB;"
      "jne ThereIsNoR;"

    "isRafterAorB:"
      "mov edx, 1;"
      "inc ebx;" 
      "inc ecx;"
      "jmp loop;"

   "ThereIsNoR:"
      "inc ebx;"
      "inc edx;"
      "mov al,[ebx];"
      "or al, al;"
      "jz ThereisNoChars;"
      "cmp al, 'r';"
      "je Finish;"
      "jmp ThereIsNoR;"

   "ThereisNoChars:"
     "mov ecx, 0;"
     "mov edx, 0;"
     "jmp Finish;"

    "Finish:"
      "mov %0, ecx;"
      "mov %1, edx;"

    ".att_syntax prefix;"
    :"=r" (x), "=r" (y)
    :"r" (s)
    :"eax", "ebx", "ecx", "edx"
  );

  printf("%d, %d \n", x, y);
  return 0;
}

它显示预期结果(5, 10)。这意味着,匹配的正则表达式从 5 个位置开始,长度为 10


首先,你对自己的要求有点不清楚。当我第一次读你的文章时,看起来你正在尝试用汇编程序()编写一个完整的“正则表达式”例程。但仔细观察,似乎您真正所做的只是在汇编器中“硬编码”这个非常具体的正则表达式搜索。这种误解可能就是这个问题没有得到任何答复的原因。

其次,你应该与this guy谁显然和你在同一个班级。也许你们两个可以分享笔记。

第三,有人应该与你的导师讨论他的作业。使用 gcc 的“内联汇编”来教授汇编可能是最难的方法。他讨厌他的学生吗?我对他提供的“模板”印象不深,(显然?)您不允许更改。我可以看到至少有六件事是我要改变的。

第四,你说正则表达式字符串“[ab][^r]+r”应该打印出来5, 10对于“fqr b qabxx xryc pqr”。我不知道怎么会这样。比赛确实从(从零开始)5 开始,但不是在位置 10 结束:

          1         2
0123456789012345678901
fqr  b qabxx  xryc pqr
     ^         ^
   start      end

末尾是位置 15。匹配的字符串 (b qabxx xr) is 11个字符长,所以显然您并不是在寻找长度。第二个“起点”出现在位置 8,第三个“起点”出现在位置 9,并且还有多个可能的终点。这些都没有解释你应该在哪里得到“10”。我假设你的意思是5, 15.

综上所述,处理[ab][^r]+r基本上分为 3 个部分:

  1. [ab]查找“a”或“b”。如果找不到字符串末尾,则在遇到字符串结尾时错误退出。
  2. [^r]+如果 (1) 后面紧跟着的字母是“r”,则转到 1。
  3. r遍历字符串的其余部分并在下一个“r”处成功退出,或者在字符串末尾错误退出。

如果您不明白为什么是这些部分,请尝试使用https://regex101.com/r/E3nI1F/1(它可以让您尝试各种正则表达式搜索字符串以查看找到的内容)。

看看您当前的代码,我认为您没有正确处理(2)或(3)(实际上,我认为您根本没有处理它们)。虽然我会在您的代码中更改其他内容,但也许调整应该等到代码正常工作为止。

鉴于这是家庭作业,我不热衷于仅仅发布我的代码。如果你只是复制/粘贴我的作品,你就不会学到任何东西。

如果您在添加 2 和 3 的工作后想编辑您的问题,我可以再次审核或提供更多建议。如果您发布工作副本,我可以分享我的副本,我们可以对它们进行比较。

----------- 编辑 1 --------------

我的老师似乎并不讨厌我们

哦?考虑这段代码(您的简化版本):

asm volatile (
   "xor %0, %0;"
   "mov %1, %2"
   :"=r" (x), "=r" (y)
   :"r" (s));

看起来很简单,对吧?清零x,并复制s to y。然而,由于所谓的“早期破坏”(参见'&' on https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html), 这是possible(不保证)优化时,gcc 将为 %0 和 %2 (或者可能是 %1 和 %2)选择相同的寄存器。因此,当您将 %0 清零时,您也可能将 %2 清零。

可以通过添加 & 符号以确保不重叠来解决此问题:

:"=&r" (x), "=&r" (y)

但你期望如何know这?了解这个细节并不能帮助你学习汇编程序。这只是 gcc 的内联汇编如何工作的一个奇怪的怪癖。如果您正在编写一个实际的汇编例程(这是我推荐的),您永远不需要知道这一点。

如果您使用符号名称,这不是更容易阅读吗?

asm volatile (
   "xor %[x], %[x];"
   "mov %[y], %[s]"
   : [x] "=&r" (x), [y] "=&r" (y)
   : [s] "r" (s));

I发现它更容易阅读。但这是另一件与汇编语言无关的事情。这只是一个关于如何在使用 gcc 时将内联 asm 推入 c 代码的技巧(你应该这样做几乎从不 do).

还有什么?此模板的一些其他问题:volatile限定符不属于这里。它缺少"cc"破坏。还有"memory"破坏。最终你会破坏比你需要的更多的寄存器。哦,为什么不直接告诉人们用-masm=intel并避免“.intel_syntax noprefix;”和“.att_syntax 前缀;”垃圾(还有更多海湾合作委员会的怪癖)。

使用汇编语言can有用的。我并不是想说事实并非如此。但尝试使用 gcc 的内联汇编充满了怪癖。由于用纯汇编程序编写的函数可以从 C 代码中调用,而且该方法没有这些问题,所以我只能得出结论,你被迫这样做是因为你对他/她很刻薄并且他/她讨厌你。

----------- 编辑2 --------------

既然您已经发布了工作代码(假设您已经修复了"arb r"),让我提供我的:

#include <stdio.h>

int main(int argc, char *argv[]) 
{
  const char *s = "fqr  b qabxx  xryc pqr"; // Succeeds with 5,11

  int x, y;

  // Assumes s is not NULL.
  // Return y=-1 on not found.

  asm volatile (
  ".intel_syntax noprefix\n\t"

     "lea ebx, [%2-1]\n\t"  // ebx is pointer to next character.
     "mov ecx, %2\n\t"      // Save this since we aren't using earlyclobber...
     "mov %1, -1\n"         // ...so at this point, %2 might be dead.

  // Note that ebx starts at s-1.

  "Phase1:\n\t"
     "inc ebx\n\t"
     "mov al, [ebx]\n\t" // Read next byte.

     "test al, al\n\t" 
     "jz Done\n\t"       // End of string.

     // Check for [ab]
     "cmp al, 'a'\n\t" 
     "je Phase2\n\t"

     "cmp al, 'b'\n\t"
     "jne Phase1\n"

     // Phase 2 - Found [ab], check for [^r]+
  "Phase2:\n\t"
     "mov al, byte ptr [ebx+1]\n\t"

     "test al, al\n\t" 
     "jz Done\n\t"     // End of string.

     "cmp al, 'r'\n\t"
     "je Phase1\n\t"   // Found [^r]+, go look for another [ab]

     "mov %0, ebx\n\t"

     // Found [ab], and no [^r]+.  Find r.
     "mov ebx, 1\n"

  "Phase3:\n\t"
     "mov al, [%0 + ebx]\n\t" // Read next byte.
     "inc ebx\n\t"

     "test al, al\n\t" 
     "jz Done\n\t"     // End of string.

     "cmp al, 'r'\n\t"
     "jne Phase3\n\t"

     // Found r.
     "sub %0, ecx\n\t" // Set (x)
     "mov %1, ebx\n"

  "Done:\n"

  ".att_syntax prefix"
  :"=r" (x), "=r" (y)
  :"r" (s)
  :"eax", "ebx", "ecx", "edx"
  );

  printf("%d, %d \n", x, y);
  return 0; 
}

它更短,并且不需要那么多寄存器(没有 edx)。虽然它还可以进一步调整,但它是解决家庭作业问题的可靠解决方案。

如果你被允许改变框架,它可能会好一点:

   // Returns y = -1 if no regex match is found.

  __asm__ (
      // ---------------------------------
      // Phase1 - look for [ab]

      "mov %[x], %[s]\n"   // Pointer to next char to read

   "Phase1:\n\t"
      "mov al, [%[x]]\n\t" // Read next byte

      "test al, al\n\t" 
      "jz NotFound\n\t"    // Hit end of string

      "inc %[x]\n\t"

      "cmp al, 'a'\n\t" 
      "je Phase2\n\t"

      "cmp al, 'b'\n\t"
      "jne Phase1\n"

      // ---------------------------------
      // Phase2 - Found [ab], Check for [^r]+
   "Phase2:\n\t"

      // x is pointing to the byte after [ab]
      "mov al, [%[x]]\n\t"  // Read next byte.

      "test al, al\n\t" 
      "jz NotFound\n\t"     // Hit end of string

      "cmp al, 'r'\n\t"
      "je Phase1\n\t"  // Found [^r]+, go look for another [ab]

      // ---------------------------------
      // Phase3 - Found [ab], and no [^r]+.  Now find r.

      // x went 1 too far back in Phase1
      "dec %[x]\n\t"

      // We know there is 1 non-r character after [ab]
      "mov %[y], 1\n"

   "Phase3:\n\t"
      "mov al, [%[x] + %[y]]\n\t" // Read next byte.
      "inc %[y]\n\t"

      "test al, al\n\t" 
      "jz NotFound\n\t"     // End of string.

      "cmp al, 'r'\n\t"
      "jne Phase3\n\t"

      // Found +r.
      "sub %[x], %[s]\n\t"  // Set x to offset
      "jmp Done\n"

   "NotFound:\n\t"
      "mov %[y], -1\n"

   "Done:"

   : [x] "=&r" (x), [y] "=&r" (y)
   : [s] "r" (s)
   : "eax", "cc", "memory");

主要变化是:

  1. 假设代码是用-masm=intel.
  2. 更改自"=r" to "=&r"。这保证了x, y and s所有这些最终都在单独的寄存器中。
  3. 使用符号名称。而不是参考x as %0,我们可以使用这个名字%[x].
  4. 由于此代码读取内存并修改标志,因此我添加了“cc”和“内存”破坏者。
  5. 删除不需要的volatile.

这会破坏更少的寄存器(只有 eax)。虽然使用寄存器并不是“坏”(没有它们很难做很多事情),但是您保留的寄存器越多,以便在您的应用程序中使用。asm,编译器在调用代码之前释放这些寄存器所需要做的工作就越多。自从x, y and s are already在寄存器中(由于"r"),利用它们可以简化代码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在汇编中实现正则表达式“[ab][^r]+r]”的匹配器 的相关文章

随机推荐