[llvm-ml] Implement support for MASM's extern directive
authorAlan Zhao <ayzhao@google.com>
Tue, 10 May 2022 18:19:45 +0000 (14:19 -0400)
committerEric Astor <epastor@google.com>
Tue, 10 May 2022 18:36:06 +0000 (14:36 -0400)
The EXTERN keyword defines external symbols in MASM.

Credit goes to epastor@ for implementing most of the logic; I (ayzhao@)
added some bugfixes and tests.

[0]: https://docs.microsoft.com/en-us/cpp/assembler/masm/extern-masm?view=msvc-170

Reviewed By: epastor

Submitted By: epastor

Differential Revision: https://reviews.llvm.org/D125273

llvm/lib/MC/MCParser/MasmParser.cpp
llvm/test/tools/llvm-ml/extern.asm [new file with mode: 0644]
llvm/test/tools/llvm-ml/extern_errors.asm [new file with mode: 0644]
llvm/test/tools/llvm-ml/indirect_branch.asm

index be478e2..da84a91 100644 (file)
@@ -976,6 +976,8 @@ private:
   bool parseDirectiveEnds(StringRef Name, SMLoc NameLoc);
   bool parseDirectiveNestedEnds();
 
+  bool parseDirectiveExtern();
+
   /// Parse a directive like ".globl" which accepts a single symbol (which
   /// should be a label or an external).
   bool parseDirectiveSymbolAttribute(MCSymbolAttr Attr);
@@ -2405,8 +2407,7 @@ bool MasmParser::parseStatement(ParseStatementInfo &Info,
     case DK_ORG:
       return parseDirectiveOrg();
     case DK_EXTERN:
-      eatToEndOfStatement(); // .extern is the default, ignore it.
-      return false;
+      return parseDirectiveExtern();
     case DK_PUBLIC:
       return parseDirectiveSymbolAttribute(MCSA_Global);
     case DK_COMM:
@@ -6019,6 +6020,37 @@ bool MasmParser::parseDirectivePurgeMacro(SMLoc DirectiveLoc) {
   return false;
 }
 
+bool MasmParser::parseDirectiveExtern() {
+  // .extern is the default - but we still need to take any provided type info.
+  auto parseOp = [&]() -> bool {
+    StringRef Name;
+    SMLoc NameLoc = getTok().getLoc();
+    if (parseIdentifier(Name))
+      return Error(NameLoc, "expected name");
+    if (parseToken(AsmToken::Colon))
+      return true;
+
+    StringRef TypeName;
+    SMLoc TypeLoc = getTok().getLoc();
+    if (parseIdentifier(TypeName))
+      return Error(TypeLoc, "expected type");
+    AsmTypeInfo Type;
+    if (lookUpType(TypeName, Type))
+      return Error(TypeLoc, "unrecognized type");
+    KnownType[Name.lower()] = Type;
+
+    MCSymbol *Sym = getContext().getOrCreateSymbol(Name);
+    Sym->setExternal(true);
+    getStreamer().emitSymbolAttribute(Sym, MCSA_Extern);
+
+    return false;
+  };
+
+  if (parseMany(parseOp))
+    return addErrorSuffix(" in directive 'extern'");
+  return false;
+}
+
 /// parseDirectiveSymbolAttribute
 ///  ::= { ".globl", ".weak", ... } [ identifier ( , identifier )* ]
 bool MasmParser::parseDirectiveSymbolAttribute(MCSymbolAttr Attr) {
diff --git a/llvm/test/tools/llvm-ml/extern.asm b/llvm/test/tools/llvm-ml/extern.asm
new file mode 100644 (file)
index 0000000..87cfba4
--- /dev/null
@@ -0,0 +1,17 @@
+; RUN: llvm-ml -m32 -filetype=s %s /Fo - | FileCheck %s --check-prefixes=CHECK,CHECK-32
+; RUN: llvm-ml -m64 -filetype=s %s /Fo - | FileCheck %s --check-prefixes=CHECK,CHECK-64
+
+extern foo : dword, bar : word
+; CHECK: .extern foo
+; CHECK: .extern bar
+
+.code
+mov ebx, foo
+; CHECK-32: mov ebx, dword ptr [foo]
+; CHECK-64: mov ebx, dword ptr [rip + foo]
+
+mov bx, bar
+; CHECK-32: mov bx, word ptr [bar]
+; CHECK-64: mov bx, word ptr [rip + bar]
+
+END
diff --git a/llvm/test/tools/llvm-ml/extern_errors.asm b/llvm/test/tools/llvm-ml/extern_errors.asm
new file mode 100644 (file)
index 0000000..72ce8d8
--- /dev/null
@@ -0,0 +1,22 @@
+; RUN: not llvm-ml -filetype=s %s /Fo /dev/null 2>&1 | FileCheck %s --implicit-check-not=error:
+
+; CHECK: :[[# @LINE + 1]]:8: error: expected name in directive 'extern'
+extern 123
+
+; CHECK: :[[# @LINE + 1]]:14: error: expected type in directive 'extern'
+extern foo0 :
+
+; CHECK: :[[# @LINE + 1]]:15: error: unrecognized type in directive 'extern'
+extern bar0 : typedoesnotexist
+
+extern foo1 : dword, bar1 : word
+
+.code
+
+; CHECK: :[[# @LINE + 1]]:1: error: invalid operand for instruction
+mov bx, foo1
+
+; CHECK: :[[# @LINE + 1]]:1: error: invalid operand for instruction
+mov bl, bar1
+
+END
index c4b28ba..391153b 100644 (file)
@@ -1,6 +1,13 @@
 ; RUN: llvm-ml -m64 -filetype=s %s /Fo - | FileCheck %s --check-prefixes=CHECK-64,CHECK
 ; RUN: llvm-ml -m32 -filetype=s %s /Fo - | FileCheck %s --check-prefixes=CHECK-32,CHECK
 
+ifdef rax
+  extern fn_ref_extern : qword
+else
+  extern fn_ref_extern : dword
+endif
+extern fn_ref_extern_word : word
+
 .data
 
 ifdef rax
@@ -186,3 +193,58 @@ t16:
 je [t16];
 ; CHECK-LABEL: t16:
 ; CHECK: je t16
+
+t17:
+call fn_ref_extern
+jmp fn_ref_extern
+; CHECK-LABEL: t17:
+; CHECK-64: call qword ptr [rip + fn_ref_extern]
+; CHECK-64: jmp qword ptr [rip + fn_ref_extern]
+; CHECK-32: call dword ptr [fn_ref_extern]
+; CHECK-32: jmp dword ptr [fn_ref_extern]
+
+t18:
+call [fn_ref_extern]
+jmp [fn_ref_extern]
+; CHECK-LABEL: t18:
+; CHECK-64: call qword ptr [rip + fn_ref_extern]
+; CHECK-64: jmp qword ptr [rip + fn_ref_extern]
+; CHECK-32: call dword ptr [fn_ref_extern]
+; CHECK-32: jmp dword ptr [fn_ref_extern]
+
+ifdef rax
+  t19:
+  call qword ptr [fn_ref_extern]
+  jmp qword ptr [fn_ref_extern]
+  ; CHECK-64-LABEL: t19:
+  ; CHECK-64: call qword ptr [rip + fn_ref_extern]
+  ; CHECK-64: jmp qword ptr [rip + fn_ref_extern]
+else
+  t19:
+  call dword ptr [fn_ref_extern]
+  jmp dword ptr [fn_ref_extern]
+  ; CHECK-32-LABEL: t19:
+  ; CHECK-32: call dword ptr [fn_ref_extern]
+  ; CHECK-32: jmp dword ptr [fn_ref_extern]
+
+  t20:
+  call fn_ref_extern_word
+  jmp fn_ref_extern_word
+  ; CHECK-32-LABEL: t20:
+  ; CHECK-32: call word ptr [fn_ref_extern_word]
+  ; CHECK-32-NEXT: jmp word ptr [fn_ref_extern_word]
+
+  t21:
+  call [fn_ref_extern_word]
+  jmp [fn_ref_extern_word]
+  ; CHECK-32-LABEL: t21:
+  ; CHECK-32: call word ptr [fn_ref_extern_word]
+  ; CHECK-32-NEXT: jmp word ptr [fn_ref_extern_word]
+
+  t22:
+  call word ptr [fn_ref_extern_word]
+  jmp word ptr [fn_ref_extern_word]
+  ; CHECK-32-LABEL: t22:
+  ; CHECK-32: call word ptr [fn_ref_extern_word]
+  ; CHECK-32-NEXT: jmp word ptr [fn_ref_extern_word]
+endif