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 @@ -128,9 +128,11 @@ struct StructInfo { StringRef Name; bool IsUnion = false; + bool Initializable = true; unsigned Alignment = 0; - unsigned Size = 0; unsigned AlignmentSize = 0; + Optional NextFieldOffset; + unsigned Size = 0; std::vector Fields; StringMap FieldsByName; @@ -322,7 +324,7 @@ struct FieldInfo { // Offset of the field within the containing STRUCT. - size_t Offset = 0; + unsigned Offset = 0; // Total size of the field (= LengthOf * Type). unsigned SizeOf = 0; @@ -346,9 +348,13 @@ FieldInfo &Field = Fields.back(); if (IsUnion) { Field.Offset = 0; + } else if (NextFieldOffset.hasValue()) { + Field.Offset = NextFieldOffset.getValue(); + NextFieldOffset.reset(); + Size = std::max(Size, Field.Offset); } else { - Size = llvm::alignTo(Size, std::min(Alignment, FieldAlignmentSize)); - Field.Offset = Size; + Field.Offset = llvm::alignTo(Size, std::min(Alignment, FieldAlignmentSize)); + Size = std::max(Size, Field.Offset); } AlignmentSize = std::max(AlignmentSize, FieldAlignmentSize); return Field; @@ -665,6 +671,7 @@ DK_REAL8, DK_REAL10, DK_ALIGN, + DK_EVEN, DK_ORG, DK_ENDR, DK_EXTERN, @@ -867,8 +874,11 @@ bool parseDirectiveEquate(StringRef IDVal, StringRef Name, DirectiveKind DirKind); - bool parseDirectiveOrg(); // ".org" + bool parseDirectiveOrg(); // "org" + + bool emitAlignTo(int64_t Alignment); bool parseDirectiveAlign(); // "align" + bool parseDirectiveEven(); // "even" // ".file", ".line", ".loc", ".stabs" bool parseDirectiveFile(SMLoc DirectiveLoc); @@ -2283,6 +2293,8 @@ return parseDirectiveNestedEnds(); case DK_ALIGN: return parseDirectiveAlign(); + case DK_EVEN: + return parseDirectiveEven(); case DK_ORG: return parseDirectiveOrg(); case DK_EXTERN: @@ -3524,10 +3536,7 @@ Field.SizeOf = Field.Type * IntInfo.Values.size(); Field.LengthOf = IntInfo.Values.size(); - if (Struct.IsUnion) - Struct.Size = std::max(Struct.Size, Field.SizeOf); - else - Struct.Size += Field.SizeOf; + Struct.Size = std::max(Struct.Size, Field.Offset + Field.SizeOf); return false; } @@ -3730,10 +3739,7 @@ Field.Type = RealInfo.AsIntValues.back().getBitWidth() / 8; Field.LengthOf = RealInfo.AsIntValues.size(); Field.SizeOf = Field.Type * Field.LengthOf; - if (Struct.IsUnion) - Struct.Size = std::max(Struct.Size, Field.SizeOf); - else - Struct.Size += Field.SizeOf; + Struct.Size = std::max(Struct.Size, Field.Offset + Field.SizeOf); return false; } @@ -4142,14 +4148,16 @@ const StructFieldInfo &Contents, const StructFieldInfo &Initializer) { for (const auto &Init : Initializer.Initializers) { - emitStructInitializer(Contents.Structure, Init); + if (emitStructInitializer(Contents.Structure, Init)) + return true; } // Default-initialize all remaining values. for (auto It = Contents.Initializers.begin() + Initializer.Initializers.size(); It != Contents.Initializers.end(); ++It) { const auto &Init = *It; - emitStructInitializer(Contents.Structure, Init); + if (emitStructInitializer(Contents.Structure, Init)) + return true; } return false; } @@ -4172,6 +4180,10 @@ bool MasmParser::emitStructInitializer(const StructInfo &Structure, const StructInitializer &Initializer) { + if (!Structure.Initializable) + return Error(getLexer().getLoc(), + "cannot initialize a value of type '" + Structure.Name + + "'; 'org' was used in the type's declaration"); size_t Index = 0, Offset = 0; for (const auto &Init : Initializer.FieldInitializers) { const auto &Field = Structure.Fields[Index++]; @@ -4228,10 +4240,7 @@ Field.LengthOf = StructInfo.Initializers.size(); Field.SizeOf = Field.Type * Field.LengthOf; - if (OwningStruct.IsUnion) - OwningStruct.Size = std::max(OwningStruct.Size, Field.SizeOf); - else - OwningStruct.Size += Field.SizeOf; + OwningStruct.Size = std::max(OwningStruct.Size, Field.Offset + Field.SizeOf); return false; } @@ -4390,17 +4399,28 @@ ParentStruct.FieldsByName[FieldByName.getKey()] = FieldByName.getValue() + OldFields; } - if (!ParentStruct.IsUnion) { - for (auto FieldIter = ParentStruct.Fields.begin() + OldFields; - FieldIter != ParentStruct.Fields.end(); ++FieldIter) { - FieldIter->Offset += ParentStruct.Size; + + unsigned FirstFieldOffset = 0; + if (!Structure.Fields.empty() && !ParentStruct.IsUnion) { + if (ParentStruct.NextFieldOffset.hasValue()) { + FirstFieldOffset = ParentStruct.NextFieldOffset.getValue(); + } else { + FirstFieldOffset = + llvm::alignTo(ParentStruct.Size, std::min(ParentStruct.Alignment, + Structure.AlignmentSize)); } } - if (ParentStruct.IsUnion) + if (ParentStruct.IsUnion) { ParentStruct.Size = std::max(ParentStruct.Size, Structure.Size); - else - ParentStruct.Size += Structure.Size; + } else { + for (auto FieldIter = ParentStruct.Fields.begin() + OldFields; + FieldIter != ParentStruct.Fields.end(); ++FieldIter) { + FieldIter->Offset += FirstFieldOffset; + } + ParentStruct.Size = + std::max(ParentStruct.Size, FirstFieldOffset + Structure.Size); + } } else { FieldInfo &Field = ParentStruct.addField(Structure.Name, FT_STRUCT, Structure.AlignmentSize); @@ -4409,10 +4429,8 @@ Field.LengthOf = 1; Field.SizeOf = Structure.Size; - if (ParentStruct.IsUnion) - ParentStruct.Size = std::max(ParentStruct.Size, Field.SizeOf); - else - ParentStruct.Size += Field.SizeOf; + ParentStruct.Size = + std::max(ParentStruct.Size, Field.Offset + Field.SizeOf); StructInfo.Structure = Structure; StructInfo.Initializers.emplace_back(); @@ -4426,33 +4444,78 @@ } /// parseDirectiveOrg -/// ::= .org expression [ , expression ] +/// ::= org expression bool MasmParser::parseDirectiveOrg() { const MCExpr *Offset; SMLoc OffsetLoc = Lexer.getLoc(); if (checkForValidSection() || parseExpression(Offset)) return true; - - // Parse optional fill expression. - int64_t FillExpr = 0; - if (parseOptionalToken(AsmToken::Comma)) - if (parseAbsoluteExpression(FillExpr)) - return addErrorSuffix(" in '.org' directive"); if (parseToken(AsmToken::EndOfStatement)) - return addErrorSuffix(" in '.org' directive"); + return addErrorSuffix(" in 'org' directive"); + + if (StructInProgress.empty()) { + // Not in a struct; change the offset for the next instruction or data + if (checkForValidSection()) + return addErrorSuffix(" in 'org' directive"); + + getStreamer().emitValueToOffset(Offset, 0, OffsetLoc); + } else { + // Offset the next field of this struct + StructInfo &Structure = StructInProgress.back(); + int64_t OffsetRes; + if (!Offset->evaluateAsAbsolute(OffsetRes, getStreamer().getAssemblerPtr())) + return Error(OffsetLoc, + "expected absolute expression in 'org' directive"); + if (OffsetRes < 0) + return Error( + OffsetLoc, + "expected non-negative value in struct's 'org' directive; was " + + std::to_string(OffsetRes)); + Structure.NextFieldOffset = static_cast(OffsetRes); + + // ORG-affected structures cannot be initialized + Structure.Initializable = false; + } - getStreamer().emitValueToOffset(Offset, FillExpr, OffsetLoc); return false; } +bool MasmParser::emitAlignTo(int64_t Alignment) { + // Always emit an alignment here even if we throw an error. + bool ReturnVal = false; + + if (StructInProgress.empty()) { + // Not in a struct; align the next instruction or data + if (checkForValidSection()) + return true; + + // Check whether we should use optimal code alignment for this align + // directive. + const MCSection *Section = getStreamer().getCurrentSectionOnly(); + assert(Section && "must have section to emit alignment"); + if (Section->UseCodeAlign()) { + getStreamer().emitCodeAlignment(Alignment, /*MaxBytesToEmit=*/0); + } else { + // FIXME: Target specific behavior about how the "extra" bytes are filled. + getStreamer().emitValueToAlignment(Alignment, /*Value=*/0, + /*ValueSize=*/1, + /*MaxBytesToEmit=*/0); + } + } else { + // Align the next field of this struct + StructInfo &Structure = StructInProgress.back(); + Structure.NextFieldOffset = llvm::alignTo(Structure.Size, Alignment); + } + + return ReturnVal; +} + /// parseDirectiveAlign /// ::= align expression bool MasmParser::parseDirectiveAlign() { SMLoc AlignmentLoc = getLexer().getLoc(); int64_t Alignment; - if (checkForValidSection()) - return addErrorSuffix(" in align directive"); // Ignore empty 'align' directives. if (getTok().is(AsmToken::EndOfStatement)) { Warning(AlignmentLoc, "align directive with no operand is ignored"); @@ -4462,27 +4525,34 @@ parseToken(AsmToken::EndOfStatement)) return addErrorSuffix(" in align directive"); - // Always emit an alignment here even if we thrown an error. + // Always emit an alignment here even if we throw an error. bool ReturnVal = false; - // Reject alignments that aren't either a power of two or zero, for gas + // Reject alignments that aren't either a power of two or zero, for ML.exe // compatibility. Alignment of zero is silently rounded up to one. if (Alignment == 0) Alignment = 1; if (!isPowerOf2_64(Alignment)) - ReturnVal |= Error(AlignmentLoc, "alignment must be a power of 2"); - - // Check whether we should use optimal code alignment for this align - // directive. - const MCSection *Section = getStreamer().getCurrentSectionOnly(); - assert(Section && "must have section to emit alignment"); - if (Section->UseCodeAlign()) { - getStreamer().emitCodeAlignment(Alignment, /*MaxBytesToEmit=*/0); - } else { - // FIXME: Target specific behavior about how the "extra" bytes are filled. - getStreamer().emitValueToAlignment(Alignment, /*Value=*/0, /*ValueSize=*/1, - /*MaxBytesToEmit=*/0); - } + ReturnVal |= Error(AlignmentLoc, "alignment must be a power of 2; was " + + std::to_string(Alignment)); + + if (emitAlignTo(Alignment)) + ReturnVal |= addErrorSuffix(" in align directive"); + + return ReturnVal; +} + +/// parseDirectiveEven +/// ::= even +bool MasmParser::parseDirectiveEven() { + if (parseToken(AsmToken::EndOfStatement)) + return addErrorSuffix(" in even directive"); + + // Always emit an alignment here even if we throw an error. + bool ReturnVal = false; + + if (emitAlignTo(2)) + ReturnVal |= addErrorSuffix(" in even directive"); return ReturnVal; } @@ -6381,7 +6451,8 @@ DirectiveKindMap["real8"] = DK_REAL8; DirectiveKindMap["real10"] = DK_REAL10; DirectiveKindMap["align"] = DK_ALIGN; - // DirectiveKindMap[".org"] = DK_ORG; + DirectiveKindMap["even"] = DK_EVEN; + DirectiveKindMap["org"] = DK_ORG; DirectiveKindMap["extern"] = DK_EXTERN; DirectiveKindMap["public"] = DK_PUBLIC; // DirectiveKindMap[".comm"] = DK_COMM; diff --git a/llvm/test/tools/llvm-ml/align_directives.asm b/llvm/test/tools/llvm-ml/align_directives.asm new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-ml/align_directives.asm @@ -0,0 +1,116 @@ +; RUN: llvm-ml -filetype=s %s /Fo - | FileCheck %s + +.data + +align_test: +ALIGN 16 +; CHECK-LABEL: align_test: +; CHECK-NEXT: .p2align 4 + +org_test: +ORG 256 +; CHECK-LABEL: org_test: +; CHECK-NEXT: .org 256, 0 + +align_struct STRUCT + BYTE ? + + ALIGN 4 + x BYTE ? + x_succ BYTE ? + BYTE ? + + ALIGN 2 + y BYTE ? + y_succ BYTE ? + + ALIGN 1 + z BYTE ? + + EVEN + q BYTE ? +align_struct ENDS + +struct_align_data ALIGN_STRUCT <101, 102, 103, 104, 105, 106, 107, 108> +; CHECK-LABEL: struct_align_data: +; CHECK-NEXT: .byte 101 +; CHECK-NEXT: .zero 3 +; CHECK-NEXT: .byte 102 +; CHECK-NEXT: .byte 103 +; CHECK-NEXT: .byte 104 +; CHECK-NEXT: .zero 1 +; CHECK-NEXT: .byte 105 +; CHECK-NEXT: .byte 106 +; CHECK-NEXT: .byte 107 +; CHECK-NEXT: .zero 1 +; CHECK-NEXT: .byte 108 + +org_struct STRUCT + x BYTE ? + x_succ BYTE ? + ORG 15 + y BYTE ? + y_succ BYTE ? + ORG 2 + z BYTE ? + z_succ BYTE ? +org_struct ENDS + +.code + +struct_align_test PROC + +x_align_test: + MOV eax, align_struct.x + MOV eax, align_struct.x_succ +; CHECK-LABEL: x_align_test: +; CHECK-NEXT: mov eax, 4 +; CHECK-NEXT: mov eax, 5 + +y_align_test: + MOV eax, align_struct.y + MOV eax, align_struct.y_succ +; CHECK-LABEL: y_align_test: +; CHECK-NEXT: mov eax, 8 +; CHECK-NEXT: mov eax, 9 + +z_align_test: + MOV eax, align_struct.z +; CHECK-LABEL: z_align_test: +; CHECK-NEXT: mov eax, 10 + +q_even_test: + MOV eax, align_struct.q +; CHECK-LABEL: q_even_test: +; CHECK-NEXT: mov eax, 12 + +size_align_test: + MOV eax, SIZEOF(align_struct) +; CHECK-LABEL: size_align_test: +; CHECK-NEXT: mov eax, 13 + + ret +struct_align_test ENDP + +struct_org_test PROC +; CHECK-LABEL: struct_org_test: + +field_positions: + MOV eax, org_struct.x + MOV eax, org_struct.x_succ + MOV eax, org_struct.y + MOV eax, org_struct.y_succ + MOV eax, org_struct.z + MOV eax, org_struct.z_succ +; CHECK-LABEL: field_positions: +; CHECK-NEXT: mov eax, 0 +; CHECK-NEXT: mov eax, 1 +; CHECK-NEXT: mov eax, 15 +; CHECK-NEXT: mov eax, 16 +; CHECK-NEXT: mov eax, 2 +; CHECK-NEXT: mov eax, 3 + + ret +struct_org_test ENDP + +end diff --git a/llvm/test/tools/llvm-ml/align_errors.asm b/llvm/test/tools/llvm-ml/align_errors.asm new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-ml/align_errors.asm @@ -0,0 +1,21 @@ +; RUN: not llvm-ml -filetype=s %s /Fo /dev/null 2>&1 | FileCheck %s --implicit-check-not=error: + +.data +org_struct STRUCT + x BYTE ? + x_succ BYTE ? + ORG 15 + y BYTE ? + y_succ BYTE ? + +; CHECK: :[[# @LINE + 1]]:7: error: expected non-negative value in struct's 'org' directive; was -4 + ORG -4 + + z BYTE ? + z_succ BYTE ? +org_struct ENDS + +; CHECK: :[[# @LINE + 1]]:16: error: cannot initialize a value of type 'org_struct'; 'org' was used in the type's declaration +x org_struct <> + +end