diff --git a/llvm/lib/MC/MCParser/MasmParser.cpp b/llvm/lib/MC/MCParser/MasmParser.cpp --- a/llvm/lib/MC/MCParser/MasmParser.cpp +++ b/llvm/lib/MC/MCParser/MasmParser.cpp @@ -976,6 +976,8 @@ 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 @@ 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 @@ 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 --- /dev/null +++ b/llvm/test/tools/llvm-ml/extern.asm @@ -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 --- /dev/null +++ b/llvm/test/tools/llvm-ml/extern_errors.asm @@ -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 diff --git a/llvm/test/tools/llvm-ml/indirect_branch.asm b/llvm/test/tools/llvm-ml/indirect_branch.asm --- a/llvm/test/tools/llvm-ml/indirect_branch.asm +++ b/llvm/test/tools/llvm-ml/indirect_branch.asm @@ -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 @@ 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