14 changes: 10 additions & 4 deletions flang/lib/Semantics/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ class ArgumentAnalyzer {
// or procedure pointer reference in a ProcedureDesignator.
MaybeExpr ExpressionAnalyzer::Designate(DataRef &&ref) {
const Symbol &last{ref.GetLastSymbol()};
const Symbol &symbol{BypassGeneric(last).GetUltimate()};
const Symbol &specific{BypassGeneric(last)};
const Symbol &symbol{specific.GetUltimate()};
if (semantics::IsProcedure(symbol)) {
if (symbol.attrs().test(semantics::Attr::ABSTRACT)) {
Say("Abstract procedure interface '%s' may not be used as a designator"_err_en_US,
Expand All @@ -226,6 +227,10 @@ MaybeExpr ExpressionAnalyzer::Designate(DataRef &&ref) {
} else if (!symbol.attrs().test(semantics::Attr::INTRINSIC)) {
if (symbol.has<semantics::GenericDetails>()) {
Say("'%s' is not a specific procedure"_err_en_US, last.name());
} else if (IsProcedurePointer(specific)) {
// For procedure pointers, retain associations so that data accesses
// from client modules will work.
return Expr<SomeType>{ProcedureDesignator{specific}};
} else {
return Expr<SomeType>{ProcedureDesignator{symbol}};
}
Expand Down Expand Up @@ -1956,7 +1961,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::ArrayConstructor &array) {

// Check if implicit conversion of expr to the symbol type is legal (if needed),
// and make it explicit if requested.
static MaybeExpr implicitConvertTo(const semantics::Symbol &sym,
static MaybeExpr ImplicitConvertTo(const semantics::Symbol &sym,
Expr<SomeType> &&expr, bool keepConvertImplicit) {
if (!keepConvertImplicit) {
return ConvertToType(sym, std::move(expr));
Expand Down Expand Up @@ -2196,7 +2201,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(
// convert would cause a segfault. Lowering will deal with
// conditionally converting and preserving the lower bounds in this
// case.
if (MaybeExpr converted{implicitConvertTo(
if (MaybeExpr converted{ImplicitConvertTo(
*symbol, std::move(*value), IsAllocatable(*symbol))}) {
if (auto componentShape{GetShape(GetFoldingContext(), *symbol)}) {
if (auto valueShape{GetShape(GetFoldingContext(), *converted)}) {
Expand Down Expand Up @@ -4605,7 +4610,8 @@ std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc() {
}
for (std::size_t i{0}; !proc && i < actuals_.size(); ++i) {
const Symbol *generic{nullptr};
if (const Symbol *binding{FindBoundOp(oprName, i, generic, true)}) {
if (const Symbol *
binding{FindBoundOp(oprName, i, generic, /*isSubroutine=*/true)}) {
if (CheckAccessibleSymbol(scope, DEREF(generic))) {
// ignore inaccessible type-bound ASSIGNMENT(=) generic
} else if (const Symbol *
Expand Down
113 changes: 80 additions & 33 deletions flang/lib/Semantics/resolve-names.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,20 @@ class ScopeHandler : public ImplicitRulesVisitor {
return *derivedType;
}
}
} else if constexpr (std::is_same_v<ProcEntityDetails, D>) {
if (auto *d{symbol->detailsIf<GenericDetails>()}) {
if (!d->derivedType()) {
// procedure pointer with same name as a generic
auto *specific{d->specific()};
if (!specific) {
specific = &currScope().MakeSymbol(name, attrs, std::move(details));
d->set_specific(*specific);
} else {
SayAlreadyDeclared(name, *specific);
}
return *specific;
}
}
}
if (symbol->CanReplaceDetails(details)) {
// update the existing symbol
Expand Down Expand Up @@ -3035,14 +3049,26 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
return;
}
const Symbol &useUltimate{useSymbol.GetUltimate()};
const auto *useGeneric{useUltimate.detailsIf<GenericDetails>()};
if (localSymbol->has<UnknownDetails>()) {
localSymbol->set_details(UseDetails{localName, useSymbol});
localSymbol->attrs() =
useSymbol.attrs() & ~Attrs{Attr::PUBLIC, Attr::PRIVATE, Attr::SAVE};
localSymbol->implicitAttrs() =
localSymbol->attrs() & Attrs{Attr::ASYNCHRONOUS, Attr::VOLATILE};
localSymbol->flags() = useSymbol.flags();
return;
if (useGeneric && useGeneric->specific() &&
IsProcedurePointer(*useGeneric->specific())) {
// We are use-associating a generic that shadows a procedure pointer.
// Local references that might be made to that procedure pointer should
// use a UseDetails symbol for proper data addressing. So create an
// empty local generic now into which the use-associated generic may
// be copied.
localSymbol->set_details(GenericDetails{});
localSymbol->get<GenericDetails>().set_kind(useGeneric->kind());
} else { // just create UseDetails
localSymbol->set_details(UseDetails{localName, useSymbol});
localSymbol->attrs() =
useSymbol.attrs() & ~Attrs{Attr::PUBLIC, Attr::PRIVATE, Attr::SAVE};
localSymbol->implicitAttrs() =
localSymbol->attrs() & Attrs{Attr::ASYNCHRONOUS, Attr::VOLATILE};
localSymbol->flags() = useSymbol.flags();
return;
}
}

Symbol &localUltimate{localSymbol->GetUltimate()};
Expand All @@ -3066,10 +3092,7 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
// - anything other than a derived type, non-generic procedure, or
// generic procedure being combined with something other than an
// prior USE association of itself

auto *localGeneric{localUltimate.detailsIf<GenericDetails>()};
const auto *useGeneric{useUltimate.detailsIf<GenericDetails>()};

Symbol *localDerivedType{nullptr};
if (localUltimate.has<DerivedTypeDetails>()) {
localDerivedType = &localUltimate;
Expand Down Expand Up @@ -3261,6 +3284,15 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
// At this point, there must be at least one generic interface.
CHECK(localGeneric || (useGeneric && (localDerivedType || localProcedure)));

// Ensure that a use-associated specific procedure that is a procedure
// pointer is properly represented as a USE association of an entity.
if (IsProcedurePointer(useProcedure)) {
Symbol &combined{currScope().MakeSymbol(localSymbol->name(),
useProcedure->attrs(), UseDetails{localName, *useProcedure})};
combined.flags() |= useProcedure->flags();
combinedProcedure = &combined;
}

if (localGeneric) {
// Create a local copy of a previously use-associated generic so that
// it can be locally extended without corrupting the original.
Expand Down Expand Up @@ -3639,36 +3671,36 @@ void InterfaceVisitor::CheckGenericProcedures(Symbol &generic) {
}
return;
}
const Symbol &firstSpecific{specifics.front()};
bool isFunction{firstSpecific.test(Symbol::Flag::Function)};
bool isBoth{false};
const Symbol *function{nullptr};
const Symbol *subroutine{nullptr};
for (const Symbol &specific : specifics) {
if (isFunction != specific.test(Symbol::Flag::Function)) { // C1514
if (context().ShouldWarn(
if (!function && specific.test(Symbol::Flag::Function)) {
function = &specific;
} else if (!subroutine && specific.test(Symbol::Flag::Subroutine)) {
subroutine = &specific;
if (details.derivedType() &&
context().ShouldWarn(
common::LanguageFeature::SubroutineAndFunctionSpecifics)) {
SayDerivedType(generic.name(),
"Generic interface '%s' should only contain functions due to derived type with same name"_warn_en_US,
*details.derivedType()->GetUltimate().scope());
}
}
if (function && subroutine) {
if (context().ShouldWarn(common::LanguageFeature::
SubroutineAndFunctionSpecifics)) { // C1514
auto &msg{Say(generic.name(),
"Generic interface '%s' has both a function and a subroutine"_warn_en_US)};
if (isFunction) {
msg.Attach(firstSpecific.name(), "Function declaration"_en_US);
msg.Attach(specific.name(), "Subroutine declaration"_en_US);
} else {
msg.Attach(firstSpecific.name(), "Subroutine declaration"_en_US);
msg.Attach(specific.name(), "Function declaration"_en_US);
}
msg.Attach(function->name(), "Function declaration"_en_US);
msg.Attach(subroutine->name(), "Subroutine declaration"_en_US);
}
isFunction = false;
isBoth = true;
break;
}
}
if (!isFunction && details.derivedType()) {
SayDerivedType(generic.name(),
"Generic interface '%s' may only contain functions due to derived type"
" with same name"_err_en_US,
*details.derivedType()->GetUltimate().scope());
}
if (!isBoth) {
generic.set(isFunction ? Symbol::Flag::Function : Symbol::Flag::Subroutine);
if (function && !subroutine) {
generic.set(Symbol::Flag::Function);
} else if (subroutine && !function) {
generic.set(Symbol::Flag::Subroutine);
}
}

Expand Down Expand Up @@ -5079,7 +5111,22 @@ bool DeclarationVisitor::HasCycle(

Symbol &DeclarationVisitor::DeclareProcEntity(
const parser::Name &name, Attrs attrs, const Symbol *interface) {
Symbol &symbol{DeclareEntity<ProcEntityDetails>(name, attrs)};
Symbol *proc{nullptr};
if (auto *extant{FindInScope(name)}) {
if (auto *d{extant->detailsIf<GenericDetails>()}; d && !d->derivedType()) {
// procedure pointer with same name as a generic
if (auto *specific{d->specific()}) {
SayAlreadyDeclared(name, *specific);
} else {
// Create the ProcEntityDetails symbol in the scope as the "specific()"
// symbol behind an existing GenericDetails symbol of the same name.
proc = &Resolve(name,
currScope().MakeSymbol(name.source, attrs, ProcEntityDetails{}));
d->set_specific(*proc);
}
}
}
Symbol &symbol{proc ? *proc : DeclareEntity<ProcEntityDetails>(name, attrs)};
if (auto *details{symbol.detailsIf<ProcEntityDetails>()}) {
if (context().HasError(symbol)) {
} else if (HasCycle(symbol, interface)) {
Expand Down
21 changes: 17 additions & 4 deletions flang/lib/Semantics/runtime-type-info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ class RuntimeTableBuilder {
SomeExpr explicitEnum_; // Value::Genre::Explicit
SomeExpr lenParameterEnum_; // Value::Genre::LenParameter
SomeExpr scalarAssignmentEnum_; // SpecialBinding::Which::ScalarAssignment
SomeExpr
scalarAllocatableAssignmentEnum_; // SpecialBinding::Which::ScalarAllocatableAssignment
SomeExpr
scalarPointerAssignmentEnum_; // SpecialBinding::Which::ScalarPointerAssignment
SomeExpr
elementalAssignmentEnum_; // SpecialBinding::Which::ElementalAssignment
SomeExpr readFormattedEnum_; // SpecialBinding::Which::ReadFormatted
Expand All @@ -174,6 +178,9 @@ RuntimeTableBuilder::RuntimeTableBuilder(
explicitEnum_{GetEnumValue("explicit")},
lenParameterEnum_{GetEnumValue("lenparameter")},
scalarAssignmentEnum_{GetEnumValue("scalarassignment")},
scalarAllocatableAssignmentEnum_{
GetEnumValue("scalarallocatableassignment")},
scalarPointerAssignmentEnum_{GetEnumValue("scalarpointerassignment")},
elementalAssignmentEnum_{GetEnumValue("elementalassignment")},
readFormattedEnum_{GetEnumValue("readformatted")},
readUnformattedEnum_{GetEnumValue("readunformatted")},
Expand Down Expand Up @@ -1122,10 +1129,10 @@ void RuntimeTableBuilder::DescribeSpecialProc(
// Non-type-bound generic INTERFACEs and assignments from distinct
// types must not be used for component intrinsic assignment.
CHECK(proc->dummyArguments.size() == 2);
const auto t1{
const auto &ddo1{
DEREF(std::get_if<evaluate::characteristics::DummyDataObject>(
&proc->dummyArguments[0].u))
.type.type()};
&proc->dummyArguments[0].u))};
const auto t1{ddo1.type.type()};
const auto t2{
DEREF(std::get_if<evaluate::characteristics::DummyDataObject>(
&proc->dummyArguments[1].u))
Expand All @@ -1137,7 +1144,13 @@ void RuntimeTableBuilder::DescribeSpecialProc(
return;
}
which = proc->IsElemental() ? elementalAssignmentEnum_
: scalarAssignmentEnum_;
: ddo1.attrs.test(
evaluate::characteristics::DummyDataObject::Attr::Allocatable)
? scalarAllocatableAssignmentEnum_
: ddo1.attrs.test(
evaluate::characteristics::DummyDataObject::Attr::Pointer)
? scalarPointerAssignmentEnum_
: scalarAssignmentEnum_;
if (binding && binding->passName() &&
*binding->passName() == proc->dummyArguments[1].name) {
argThatMightBeDescriptor = 1;
Expand Down
3 changes: 2 additions & 1 deletion flang/lib/Semantics/symbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,9 @@ const Symbol *GenericDetails::CheckSpecific() const {
}
Symbol *GenericDetails::CheckSpecific() {
if (specific_ && !specific_->has<UseErrorDetails>()) {
const Symbol &ultimate{specific_->GetUltimate()};
for (const Symbol &proc : specificProcs_) {
if (&proc == specific_) {
if (&proc.GetUltimate() == &ultimate) {
return nullptr;
}
}
Expand Down
9 changes: 0 additions & 9 deletions flang/lib/Semantics/type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -893,13 +893,4 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const DeclTypeSpec &x) {
return o << x.AsFortran();
}

std::optional<bool> IsInteroperableIntrinsicType(
const DeclTypeSpec &type, const common::LanguageFeatureControl &features) {
if (auto dyType{evaluate::DynamicType::From(type)}) {
return IsInteroperableIntrinsicType(*dyType, &features);
} else {
return std::nullopt;
}
}

} // namespace Fortran::semantics
13 changes: 8 additions & 5 deletions flang/module/__fortran_type_info.f90
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,14 @@
end type

enum, bind(c) ! SpecialBinding::Which
enumerator :: ScalarAssignment = 1, ElementalAssignment = 2
enumerator :: ReadFormatted = 3, ReadUnformatted = 4
enumerator :: WriteFormatted = 5, WriteUnformatted = 6
enumerator :: ElementalFinal = 7, AssumedRankFinal = 8
enumerator :: ScalarFinal = 9 ! higher-rank final procedures follow
enumerator :: ScalarAssignment = 1
enumerator :: ScalarAllocatableAssignment = 2
enumerator :: ScalarPointerAssignment = 3
enumerator :: ElementalAssignment = 4
enumerator :: ReadFormatted = 5, ReadUnformatted = 6
enumerator :: WriteFormatted = 7, WriteUnformatted = 8
enumerator :: ElementalFinal = 9, AssumedRankFinal = 10
enumerator :: ScalarFinal = 11 ! higher-rank final procedures follow
end enum

type, bind(c) :: SpecialBinding
Expand Down
16 changes: 13 additions & 3 deletions flang/runtime/assign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,17 @@ RT_API_ATTRS static void Assign(
// the Assign() is invoked recursively for component-per-component
// assignments.
if (to.rank() == 0) {
if (to.IsAllocatable()) {
if (const auto *special{toDerived->FindSpecialBinding(typeInfo::
SpecialBinding::Which::ScalarAllocatableAssignment)}) {
return DoScalarDefinedAssignment(to, from, *special);
}
} else if (to.IsPointer()) {
if (const auto *special{toDerived->FindSpecialBinding(
typeInfo::SpecialBinding::Which::ScalarPointerAssignment)}) {
return DoScalarDefinedAssignment(to, from, *special);
}
}
if (const auto *special{toDerived->FindSpecialBinding(
typeInfo::SpecialBinding::Which::ScalarAssignment)}) {
return DoScalarDefinedAssignment(to, from, *special);
Expand Down Expand Up @@ -417,9 +428,8 @@ RT_API_ATTRS static void Assign(
StaticDescriptor<maxRank, true, 10 /*?*/> statDesc[2];
Descriptor &toCompDesc{statDesc[0].descriptor()};
Descriptor &fromCompDesc{statDesc[1].descriptor()};
comp.CreatePointerDescriptor(toCompDesc, to, terminator, toAt);
comp.CreatePointerDescriptor(
fromCompDesc, from, terminator, fromAt);
comp.CreateTargetDescriptor(toCompDesc, to, terminator, toAt);
comp.CreateTargetDescriptor(fromCompDesc, from, terminator, fromAt);
Assign(toCompDesc, fromCompDesc, terminator, nestedFlags);
} else { // Component has intrinsic type; simply copy raw bytes
std::size_t componentByteSize{comp.SizeInBytes(to)};
Expand Down
2 changes: 1 addition & 1 deletion flang/runtime/descriptor-io.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ static RT_API_ATTRS bool DefaultComponentIO(IoStatementState &io,
// Create a descriptor for the component
StaticDescriptor<maxRank, true, 16 /*?*/> statDesc;
Descriptor &desc{statDesc.descriptor()};
component.CreatePointerDescriptor(
component.CreateTargetDescriptor(
desc, origDescriptor, terminator, origSubscripts);
return DescriptorIO<DIR>(io, desc, table);
} else {
Expand Down
5 changes: 3 additions & 2 deletions flang/runtime/format-implementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -443,8 +443,9 @@ RT_API_ATTRS int FormatControl<CONTEXT>::CueUpNextDataEdit(
if (ch != 'P') { // 1PE5.2 - comma not required (C1302)
CharType peek{Capitalize(PeekNext())};
if (peek >= 'A' && peek <= 'Z') {
if (ch == 'A' /* anticipate F'202X AT editing */ || ch == 'B' ||
ch == 'D' || ch == 'E' || ch == 'R' || ch == 'S' || ch == 'T') {
if ((ch == 'A' && peek == 'T' /* anticipate F'202X AT editing */) ||
ch == 'B' || ch == 'D' || ch == 'E' || ch == 'R' || ch == 'S' ||
ch == 'T') {
// Assume a two-letter edit descriptor
next = peek;
++offset_;
Expand Down
5 changes: 3 additions & 2 deletions flang/runtime/io-stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,9 @@ class IoStatementState {
std::size_t &byteCount) {
auto ch{GetCurrentChar(byteCount)};
bool inNamelist{mutableModes().inNamelist};
while (!ch || *ch == ' ' || *ch == '\t' || (inNamelist && *ch == '!')) {
if (ch && (*ch == ' ' || *ch == '\t')) {
while (!ch || *ch == ' ' || *ch == '\t' || *ch == '\n' ||
(inNamelist && *ch == '!')) {
if (ch && (*ch == ' ' || *ch == '\t' || *ch == '\n')) {
HandleRelativePosition(byteCount);
} else if (!AdvanceRecord()) {
return Fortran::common::nullopt;
Expand Down
4 changes: 2 additions & 2 deletions flang/runtime/namelist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,15 +362,15 @@ static RT_API_ATTRS bool HandleComponent(IoStatementState &io, Descriptor &desc,
io.HandleRelativePosition(byteCount); // skip over '('
StaticDescriptor<maxRank, true, 16> staticDesc;
Descriptor &tmpDesc{staticDesc.descriptor()};
comp->CreatePointerDescriptor(tmpDesc, source, handler);
comp->CreateTargetDescriptor(tmpDesc, source, handler);
if (!HandleSubscripts(io, desc, tmpDesc, compName)) {
return false;
}
createdDesc = true;
}
}
if (!createdDesc) {
comp->CreatePointerDescriptor(desc, source, handler);
comp->CreateTargetDescriptor(desc, source, handler);
}
if (source.rank() > 0) {
if (desc.rank() > 0) {
Expand Down
18 changes: 11 additions & 7 deletions flang/runtime/time-intrinsic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,16 +490,20 @@ void RTNAME(Etime)(const Descriptor *values, const Descriptor *time,
auto typeCode{values->type().GetCategoryAndKind()};
// ETIME values argument must have decimal range == 2.
RUNTIME_CHECK(terminator,
values->rank() == 1 && values->GetDimension(0).Extent() == 2 &&
typeCode && typeCode->first == Fortran::common::TypeCategory::Real);
values->rank() == 1 && typeCode &&
typeCode->first == Fortran::common::TypeCategory::Real);
// Only accept KIND=4 here.
int kind{typeCode->second};
RUNTIME_CHECK(terminator, kind == 4);

ApplyFloatingPointKind<StoreFloatingPointAt, void>(
kind, terminator, *values, /* atIndex = */ 0, usrTime);
ApplyFloatingPointKind<StoreFloatingPointAt, void>(
kind, terminator, *values, /* atIndex = */ 1, sysTime);
auto extent{values->GetDimension(0).Extent()};
if (extent >= 1) {
ApplyFloatingPointKind<StoreFloatingPointAt, void>(
kind, terminator, *values, /* atIndex = */ 0, usrTime);
}
if (extent >= 2) {
ApplyFloatingPointKind<StoreFloatingPointAt, void>(
kind, terminator, *values, /* atIndex = */ 1, sysTime);
}
}

if (time) {
Expand Down
9 changes: 7 additions & 2 deletions flang/runtime/type-info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ RT_API_ATTRS void Component::EstablishDescriptor(Descriptor &descriptor,
}
}

RT_API_ATTRS void Component::CreatePointerDescriptor(Descriptor &descriptor,
RT_API_ATTRS void Component::CreateTargetDescriptor(Descriptor &descriptor,
const Descriptor &container, Terminator &terminator,
const SubscriptValue *subscripts) const {
RUNTIME_CHECK(terminator, genre_ == Genre::Data);
Expand All @@ -144,7 +144,6 @@ RT_API_ATTRS void Component::CreatePointerDescriptor(Descriptor &descriptor,
} else {
descriptor.set_base_addr(container.OffsetElement<char>() + offset_);
}
descriptor.raw().attribute = CFI_attribute_pointer;
}

RT_API_ATTRS const DerivedType *DerivedType::GetParentType() const {
Expand Down Expand Up @@ -297,6 +296,12 @@ FILE *SpecialBinding::Dump(FILE *f) const {
case Which::ScalarAssignment:
std::fputs(" ScalarAssignment", f);
break;
case Which::ScalarAllocatableAssignment:
std::fputs(" ScalarAllocatableAssignment", f);
break;
case Which::ScalarPointerAssignment:
std::fputs(" ScalarPointerAssignment", f);
break;
case Which::ElementalAssignment:
std::fputs(" ElementalAssignment", f);
break;
Expand Down
22 changes: 12 additions & 10 deletions flang/runtime/type-info.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ class Component {
RT_API_ATTRS void EstablishDescriptor(
Descriptor &, const Descriptor &container, Terminator &) const;

// Creates a pointer descriptor from this component description, possibly
// Creates a descriptor from this component description, possibly
// with subscripts
RT_API_ATTRS void CreatePointerDescriptor(Descriptor &,
RT_API_ATTRS void CreateTargetDescriptor(Descriptor &,
const Descriptor &container, Terminator &,
const SubscriptValue * = nullptr) const;

Expand Down Expand Up @@ -126,14 +126,16 @@ class SpecialBinding {
enum class Which : std::uint8_t {
None = 0,
ScalarAssignment = 1,
ElementalAssignment = 2,
ReadFormatted = 3,
ReadUnformatted = 4,
WriteFormatted = 5,
WriteUnformatted = 6,
ElementalFinal = 7,
AssumedRankFinal = 8,
ScalarFinal = 9,
ScalarAllocatableAssignment = 2,
ScalarPointerAssignment = 3,
ElementalAssignment = 4,
ReadFormatted = 5,
ReadUnformatted = 6,
WriteFormatted = 7,
WriteUnformatted = 8,
ElementalFinal = 9,
AssumedRankFinal = 10,
ScalarFinal = 11,
// higher-ranked final procedures follow
};

Expand Down
6 changes: 6 additions & 0 deletions flang/test/Evaluate/fold-assumed-rank-kind.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
subroutine subr(ar)
real(8) :: ar(..)
!CHECK: PRINT *, 8_4
print *, kind(ar)
end
4 changes: 4 additions & 0 deletions flang/test/Semantics/Inputs/modfile66.cuf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module usereal2
!REAL(2) is interoperable under CUDA
real(2), bind(c) :: x
end
16 changes: 16 additions & 0 deletions flang/test/Semantics/Inputs/modfile67.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
!mod$ v1 sum:37cfecee3234c8ab
module modfile67
type::t
procedure(foo),nopass,pointer::p
end type
contains
pure function foo(n,a) result(r)
integer(4),intent(in)::n
real(4),intent(in)::a(1_8:int(n,kind=8))
logical(4)::r(1_8:int(int(max(0_8,int(n,kind=8)),kind=4),kind=8))
end
function fooptr(f)
procedure(foo)::f
type(t)::fooptr
end
end
17 changes: 17 additions & 0 deletions flang/test/Semantics/generic10.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
module m
procedure(func), pointer :: foo
interface foo
procedure :: foo
end interface
contains
function func(x)
func = x
end
end

program main
use m
!CHECK: foo => func
foo => func
end
3 changes: 3 additions & 0 deletions flang/test/Semantics/modfile66.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
! RUN: %flang_fc1 -fsyntax-only %S/Inputs/modfile66.cuf && %flang_fc1 -fsyntax-only %s
use usereal2 ! valid since x is not used
end
35 changes: 35 additions & 0 deletions flang/test/Semantics/modfile67.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
!RUN: %flang_fc1 -fsyntax-only -J%S/Inputs %s

#if 0
!modfile67.mod was produced from this source, and must be read into this
!compilation from its module file in order to truly test this fix.
module modfile67
type t
procedure(foo), nopass, pointer :: p
end type
contains
pure function foo(n,a) result(r)
integer, intent(in) :: n
real, intent(in), dimension(n) :: a
logical, dimension(size(a)) :: r
r = .false.
end
type(t) function fooptr(f)
procedure(foo) f
fooptr%p => f
end
end
#endif

program test
use modfile67
type(t) x
x = fooptr(bar) ! ensure no bogus error about procedure incompatibility
contains
pure function bar(n,a) result(r)
integer, intent(in) :: n
real, intent(in), dimension(n) :: a
logical, dimension(size(a)) :: r
r = .false.
end
end
8 changes: 4 additions & 4 deletions flang/test/Semantics/resolve24.f90
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
! RUN: %python %S/test_errors.py %s %flang_fc1
subroutine test1
!ERROR: Generic interface 'foo' has both a function and a subroutine
!WARNING: Generic interface 'foo' has both a function and a subroutine
interface foo
subroutine s1(x)
end subroutine
Expand All @@ -12,7 +12,7 @@ function f()
end subroutine

subroutine test2
!ERROR: Generic interface 'foo' has both a function and a subroutine
!WARNING: Generic interface 'foo' has both a function and a subroutine
interface foo
function t2f1(x)
end function
Expand All @@ -24,7 +24,7 @@ function t2f2(x, y)
end subroutine

module test3
!ERROR: Generic interface 'foo' has both a function and a subroutine
!WARNING: Generic interface 'foo' has both a function and a subroutine
interface foo
module procedure s
module procedure f
Expand All @@ -39,7 +39,7 @@ function f()
subroutine test4
type foo
end type
!ERROR: Generic interface 'foo' may only contain functions due to derived type with same name
!WARNING: Generic interface 'foo' should only contain functions due to derived type with same name
interface foo
subroutine s()
end subroutine
Expand Down
6 changes: 3 additions & 3 deletions flang/test/Semantics/resolve69.f90
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ subroutine s1()
!
integer, parameter :: constVal = 1
integer :: nonConstVal = 1
!ERROR: Invalid specification expression: reference to local entity 'nonconstval'
!PORTABILITY: specification expression refers to local object 'nonconstval' (initialized and saved)
character(nonConstVal) :: colonString1
character(len=20, kind=constVal + 1) :: constKindString
character(len=:, kind=constVal + 1), pointer :: constKindString1
Expand Down Expand Up @@ -53,13 +53,13 @@ function foo3()

type (derived(constVal, 3)) :: constDerivedKind
!ERROR: Value of KIND type parameter 'typekind' must be constant
!ERROR: Invalid specification expression: reference to local entity 'nonconstval'
!PORTABILITY: specification expression refers to local object 'nonconstval' (initialized and saved)
type (derived(nonConstVal, 3)) :: nonConstDerivedKind

!OK because all type-params are constants
type (derived(3, constVal)) :: constDerivedLen

!ERROR: Invalid specification expression: reference to local entity 'nonconstval'
!PORTABILITY: specification expression refers to local object 'nonconstval' (initialized and saved)
type (derived(3, nonConstVal)) :: nonConstDerivedLen
!ERROR: 'colonderivedlen' has a type derived(typekind=3_4,typelen=:) with a deferred type parameter but is neither an allocatable nor an object pointer
type (derived(3, :)) :: colonDerivedLen
Expand Down
1 change: 1 addition & 0 deletions flang/test/Semantics/resolve77.f90
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pure integer function if2(n)
block data
common /blk2/ n
data n/100/
!PORTABILITY: specification expression refers to local object 'n' (initialized and saved)
!ERROR: Automatic data object 'a' may not appear in a BLOCK DATA subprogram
real a(n)
end
Expand Down
2 changes: 1 addition & 1 deletion flang/test/Semantics/spec-expr.f90
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ subroutine s7biii(x, y)
integer :: local = 5
! OK, since "localConst" is a constant
real, dimension(localConst) :: realArray1
!ERROR: Invalid specification expression: reference to local entity 'local'
!PORTABILITY: specification expression refers to local object 'local' (initialized and saved)
real, dimension(local) :: realArray2
real, dimension(size(realArray1)) :: realArray3 ! ok
real, dimension(size(x)) :: realArray4 ! ok
Expand Down
18 changes: 9 additions & 9 deletions flang/test/Semantics/typeinfo01.f90
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ impure elemental subroutine s1(x, y)
class(t), intent(out) :: x
class(t), intent(in) :: y
end subroutine
!CHECK: .dt.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(binding=.v.t,name=.n.t,sizeinbytes=0_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=NULL(),procptr=NULL(),special=.s.t,specialbitset=4_4,hasparent=0_1,noinitializationneeded=1_1,nodestructionneeded=1_1,nofinalizationneeded=1_1)
!CHECK: .s.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:0_8 init:[specialbinding::specialbinding(which=2_1,isargdescriptorset=3_1,istypebound=1_1,isargcontiguousset=0_1,proc=s1)]
!CHECK: .dt.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(binding=.v.t,name=.n.t,sizeinbytes=0_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=NULL(),procptr=NULL(),special=.s.t,specialbitset=16_4,hasparent=0_1,noinitializationneeded=1_1,nodestructionneeded=1_1,nofinalizationneeded=1_1)
!CHECK: .s.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:0_8 init:[specialbinding::specialbinding(which=4_1,isargdescriptorset=3_1,istypebound=1_1,isargcontiguousset=0_1,proc=s1)]
!CHECK: .v.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(binding) shape: 0_8:0_8 init:[binding::binding(proc=s1,name=.n.s1)]
end module

Expand All @@ -125,8 +125,8 @@ impure elemental subroutine s3(x)
subroutine s4(x)
type(t), contiguous :: x(:,:,:)
end subroutine
!CHECK: .dt.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(binding=NULL(),name=.n.t,sizeinbytes=0_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=NULL(),procptr=NULL(),special=.s.t,specialbitset=7296_4,hasparent=0_1,noinitializationneeded=1_1,nodestructionneeded=0_1,nofinalizationneeded=0_1)
!CHECK: .s.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:3_8 init:[specialbinding::specialbinding(which=7_1,isargdescriptorset=0_1,istypebound=1_1,isargcontiguousset=0_1,proc=s3),specialbinding(which=10_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=s1),specialbinding(which=11_1,isargdescriptorset=0_1,istypebound=1_1,isargcontiguousset=1_1,proc=s2),specialbinding(which=12_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=1_1,proc=s4)]
!CHECK: .dt.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(binding=NULL(),name=.n.t,sizeinbytes=0_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=NULL(),procptr=NULL(),special=.s.t,specialbitset=29184_4,hasparent=0_1,noinitializationneeded=1_1,nodestructionneeded=0_1,nofinalizationneeded=0_1)
!CHECK: .s.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:3_8 init:[specialbinding::specialbinding(which=9_1,isargdescriptorset=0_1,istypebound=1_1,isargcontiguousset=0_1,proc=s3),specialbinding(which=12_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=s1),specialbinding(which=13_1,isargdescriptorset=0_1,istypebound=1_1,isargcontiguousset=1_1,proc=s2),specialbinding(which=14_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=1_1,proc=s4)]
end module

module m09
Expand Down Expand Up @@ -167,8 +167,8 @@ subroutine wu(x,u,iostat,iomsg)
integer, intent(out) :: iostat
character(len=*), intent(inout) :: iomsg
end subroutine
!CHECK: .dt.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(binding=.v.t,name=.n.t,sizeinbytes=0_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=NULL(),procptr=NULL(),special=.s.t,specialbitset=120_4,hasparent=0_1,noinitializationneeded=1_1,nodestructionneeded=1_1,nofinalizationneeded=1_1)
!CHECK: .s.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:3_8 init:[specialbinding::specialbinding(which=3_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=rf),specialbinding(which=4_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=ru),specialbinding(which=5_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=wf),specialbinding(which=6_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=wu)]
!CHECK: .dt.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(binding=.v.t,name=.n.t,sizeinbytes=0_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=NULL(),procptr=NULL(),special=.s.t,specialbitset=480_4,hasparent=0_1,noinitializationneeded=1_1,nodestructionneeded=1_1,nofinalizationneeded=1_1)
!CHECK: .s.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:3_8 init:[specialbinding::specialbinding(which=5_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=rf),specialbinding(which=6_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=ru),specialbinding(which=7_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=wf),specialbinding(which=8_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=wu)]
!CHECK: .v.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(binding) shape: 0_8:3_8 init:[binding::binding(proc=rf,name=.n.rf),binding(proc=ru,name=.n.ru),binding(proc=wf,name=.n.wf),binding(proc=wu,name=.n.wu)]
end module

Expand Down Expand Up @@ -216,8 +216,8 @@ subroutine wu(x,u,iostat,iomsg)
integer, intent(out) :: iostat
character(len=*), intent(inout) :: iomsg
end subroutine
!CHECK: .dt.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(binding=NULL(),name=.n.t,sizeinbytes=0_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=NULL(),procptr=NULL(),special=.s.t,specialbitset=120_4,hasparent=0_1,noinitializationneeded=1_1,nodestructionneeded=1_1,nofinalizationneeded=1_1)
!CHECK: .s.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:3_8 init:[specialbinding::specialbinding(which=3_1,isargdescriptorset=0_1,istypebound=0_1,isargcontiguousset=0_1,proc=rf),specialbinding(which=4_1,isargdescriptorset=0_1,istypebound=0_1,isargcontiguousset=0_1,proc=ru),specialbinding(which=5_1,isargdescriptorset=0_1,istypebound=0_1,isargcontiguousset=0_1,proc=wf),specialbinding(which=6_1,isargdescriptorset=0_1,istypebound=0_1,isargcontiguousset=0_1,proc=wu)]
!CHECK: .dt.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(binding=NULL(),name=.n.t,sizeinbytes=0_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=NULL(),procptr=NULL(),special=.s.t,specialbitset=480_4,hasparent=0_1,noinitializationneeded=1_1,nodestructionneeded=1_1,nofinalizationneeded=1_1)
!CHECK: .s.t, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:3_8 init:[specialbinding::specialbinding(which=5_1,isargdescriptorset=0_1,istypebound=0_1,isargcontiguousset=0_1,proc=rf),specialbinding(which=6_1,isargdescriptorset=0_1,istypebound=0_1,isargcontiguousset=0_1,proc=ru),specialbinding(which=7_1,isargdescriptorset=0_1,istypebound=0_1,isargcontiguousset=0_1,proc=wf),specialbinding(which=8_1,isargdescriptorset=0_1,istypebound=0_1,isargcontiguousset=0_1,proc=wu)]
end module

module m11
Expand Down Expand Up @@ -260,7 +260,7 @@ module m13
contains
procedure :: assign1, assign2
generic :: assignment(=) => assign1, assign2
! CHECK: .s.t1, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:0_8 init:[specialbinding::specialbinding(which=2_1,isargdescriptorset=3_1,istypebound=1_1,isargcontiguousset=0_1,proc=assign1)]
! CHECK: .s.t1, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:0_8 init:[specialbinding::specialbinding(which=4_1,isargdescriptorset=3_1,istypebound=1_1,isargcontiguousset=0_1,proc=assign1)]
end type
contains
impure elemental subroutine assign1(to, from)
Expand Down
4 changes: 2 additions & 2 deletions flang/test/Semantics/typeinfo02.f90
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ subroutine wf2(x,u,iot,v,iostat,iomsg)
character(len=*), intent(inout) :: iomsg
end subroutine
end module
!CHECK: .s.base, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:0_8 init:[specialbinding::specialbinding(which=5_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=wf1)]
!CHECK: .s.extended, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:0_8 init:[specialbinding::specialbinding(which=5_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=wf2)]
!CHECK: .s.base, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:0_8 init:[specialbinding::specialbinding(which=7_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=wf1)]
!CHECK: .s.extended, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:0_8 init:[specialbinding::specialbinding(which=7_1,isargdescriptorset=1_1,istypebound=1_1,isargcontiguousset=0_1,proc=wf2)]
2 changes: 1 addition & 1 deletion flang/test/Semantics/typeinfo04.f90
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module m
contains
final :: final
end type
!CHECK: .dt.finalizable, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(binding=NULL(),name=.n.finalizable,sizeinbytes=0_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=NULL(),procptr=NULL(),special=.s.finalizable,specialbitset=128_4,hasparent=0_1,noinitializationneeded=1_1,nodestructionneeded=0_1,nofinalizationneeded=0_1)
!CHECK: .dt.finalizable, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(binding=NULL(),name=.n.finalizable,sizeinbytes=0_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=NULL(),procptr=NULL(),special=.s.finalizable,specialbitset=512_4,hasparent=0_1,noinitializationneeded=1_1,nodestructionneeded=0_1,nofinalizationneeded=0_1)
type, abstract :: t1
end type
!CHECK: .dt.t1, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(name=.n.t1,sizeinbytes=0_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=NULL(),procptr=NULL(),specialbitset=0_4,hasparent=0_1,noinitializationneeded=1_1,nodestructionneeded=1_1,nofinalizationneeded=1_1)
Expand Down
52 changes: 52 additions & 0 deletions flang/test/Semantics/typeinfo12.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
!RUN: bbc --dump-symbols %s | FileCheck %s
!RUN: %flang_fc1 -fdebug-dump-symbols %s | FileCheck %s

! Test defined assignment with allocatable / pointer LHS arguments.
! The special bindings for the defined assignments must reflect that
! their LHS arguments are allocatables and pointers.
! (This program is executable and should print 1; 102; 3 204.)

module m
type :: base
integer :: i
contains
procedure, pass(src) :: ass1, ass2
generic :: assignment(=) => ass1, ass2
end type base
type, extends(base) :: derived
end type

!CHECK: .dt.base, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(binding=.v.base,name=.n.base,sizeinbytes=4_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=.c.base,procptr=NULL(),special=.s.base,specialbitset=12_4,hasparent=0_1,noinitializationneeded=1_1,nodestructionneeded=1_1,nofinalizationneeded=1_1)
!CHECK: .dt.derived, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(derivedtype) init:derivedtype(binding=.v.derived,name=.n.derived,sizeinbytes=4_8,uninstantiated=NULL(),kindparameter=NULL(),lenparameterkind=NULL(),component=.c.derived,procptr=NULL(),special=.s.derived,specialbitset=12_4,hasparent=1_1,noinitializationneeded=1_1,nodestructionneeded=1_1,nofinalizationneeded=1_1)
!CHECK: .s.base, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:1_8 init:[specialbinding::specialbinding(which=2_1,isargdescriptorset=3_1,istypebound=1_1,isargcontiguousset=0_1,proc=ass1),specialbinding(which=3_1,isargdescriptorset=3_1,istypebound=1_1,isargcontiguousset=0_1,proc=ass2)]
!CHECK: .s.derived, SAVE, TARGET (CompilerCreated, ReadOnly): ObjectEntity type: TYPE(specialbinding) shape: 0_8:1_8 init:[specialbinding::specialbinding(which=2_1,isargdescriptorset=3_1,istypebound=1_1,isargcontiguousset=0_1,proc=ass1),specialbinding(which=3_1,isargdescriptorset=3_1,istypebound=1_1,isargcontiguousset=0_1,proc=ass2)]

contains
subroutine ass1(res, src)
class(base), allocatable, intent(out) :: res
class(base), intent(in) :: src
allocate(res, source=src)
res%i = res%i + 100
end subroutine
subroutine ass2(res, src)
class(base), pointer, intent(in out) :: res
class(base), intent(in) :: src
allocate(res, source=src)
res%i = src%i + 200
end subroutine
end
program genext
use m
type(derived) :: od1
class(base), allocatable :: od2
class(base), pointer :: od3a, od3b
od1 = derived(1)
print *, od1%i
od2 = derived(2)
print *, od2%i
allocate(od3a)
od3a%i = 3
od3b => od3a
od3b = derived(4)
print *, od3a%i, od3b%i
end program genext
12 changes: 2 additions & 10 deletions libcxx/include/string
Original file line number Diff line number Diff line change
Expand Up @@ -749,14 +749,6 @@ struct __can_be_converted_to_string_view
struct __uninitialized_size_tag {};
struct __init_with_sentinel_tag {};

template <size_t _PaddingSize>
struct __padding {
char __padding_[_PaddingSize];
};

template <>
struct __padding<0> {};

template <class _CharT, class _Traits, class _Allocator>
class basic_string {
private:
Expand Down Expand Up @@ -861,7 +853,7 @@ private:

struct __short {
value_type __data_[__min_cap];
_LIBCPP_NO_UNIQUE_ADDRESS __padding<sizeof(value_type) - 1> __padding_;
unsigned char __padding_[sizeof(value_type) - 1];
unsigned char __size_ : 7;
unsigned char __is_long_ : 1;
};
Expand Down Expand Up @@ -913,7 +905,7 @@ private:
unsigned char __is_long_ : 1;
unsigned char __size_ : 7;
};
_LIBCPP_NO_UNIQUE_ADDRESS __padding<sizeof(value_type) - 1> __padding_;
char __padding_[sizeof(value_type) - 1];
value_type __data_[__min_cap];
};

Expand Down
2 changes: 2 additions & 0 deletions lldb/test/API/commands/statistics/basic/TestStats.py
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,7 @@ def test_order_of_options_do_not_matter(self):
f"The order of options '{options[0]}' and '{options[1]}' should not matter",
)

@skipIfWindows
def test_summary_statistics_providers(self):
"""
Test summary timing statistics is included in statistics dump when
Expand Down Expand Up @@ -960,6 +961,7 @@ def test_summary_statistics_providers(self):
self.assertIn("'totalTime':", summary_provider_str)
self.assertIn("'type': 'python'", summary_provider_str)

@skipIfWindows
def test_summary_statistics_providers_vec(self):
"""
Test summary timing statistics is included in statistics dump when
Expand Down
28 changes: 27 additions & 1 deletion llvm/docs/DeveloperPolicy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,8 @@ If you have questions or comments about these topics, please ask on the
please realize that most compiler developers are not lawyers, and therefore you
will not be getting official legal advice.

.. _LLVM Discourse forums: https://discourse.llvm.org

Copyright
---------

Expand Down Expand Up @@ -1301,4 +1303,28 @@ to move code from (e.g.) libc++ to the LLVM core without concern, but that code
cannot be moved from the LLVM core to libc++ without the copyright owner's
permission.

.. _LLVM Discourse forums: https://discourse.llvm.org
.. _ai contributions:

AI generated contributions
--------------------------

Artificial intelligence systems raise many questions around copyright that have
yet to be answered. Our policy on AI tools is guided by our copyright policy:
Contributors are responsible for ensuring that they have the right to contribute
code under the terms of our license, typically meaning that either they, their
employer, or their collaborators hold the copyright. Using AI tools to
regenerate copyrighted material does not remove the copyright, and contributors
are responsible for ensuring that such material does not appear in their
contributions.

As such, the LLVM policy is that contributors are permitted to use artificial
intelligence tools to produce contributions, provided that they have the right
to license that code under the project license. Contributions found to violate
this policy will be removed just like any other offending contribution.

While the LLVM project has a liberal policy on AI tool use, contributors are
considered responsible for their contributions. We encourage contributors to
review all generated code before sending it for review to verify its
correctness and to understand it so that they can answer questions during code
review. Reviewing and maintaining generated code that the original contributor
does not understand is not a good use of limited project resources.
7 changes: 7 additions & 0 deletions llvm/docs/FAQ.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ Yes. This is why we distribute LLVM under a less restrictive license than GPL,
as explained in the first question above.


Can I use AI coding tools, such as GitHub co-pilot, to write LLVM patches?
--------------------------------------------------------------------------
Yes, as long as the resulting work can be licensed under the project license, as
covered in the :doc:`DeveloperPolicy`. Using an AI tool to reproduce copyrighted
work does not rinse it of copyright and grant you the right to relicense it.


Source Code
===========

Expand Down
29 changes: 29 additions & 0 deletions llvm/include/llvm/SandboxIR/PassManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#ifndef LLVM_SANDBOXIR_PASSMANAGER_H
#define LLVM_SANDBOXIR_PASSMANAGER_H

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/SandboxIR/Pass.h"
#include "llvm/Support/Debug.h"
Expand Down Expand Up @@ -65,6 +66,34 @@ class FunctionPassManager final
bool runOnFunction(Function &F) final;
};

/// Owns the passes and provides an API to get a pass by its name.
class PassRegistry {
SmallVector<std::unique_ptr<Pass>, 8> Passes;
DenseMap<StringRef, Pass *> NameToPassMap;

public:
PassRegistry() = default;
/// Registers \p PassPtr and takes ownership.
Pass &registerPass(std::unique_ptr<Pass> &&PassPtr) {
auto &PassRef = *PassPtr.get();
NameToPassMap[PassRef.getName()] = &PassRef;
Passes.push_back(std::move(PassPtr));
return PassRef;
}
/// \Returns the pass with name \p Name, or null if not registered.
Pass *getPassByName(StringRef Name) const {
auto It = NameToPassMap.find(Name);
return It != NameToPassMap.end() ? It->second : nullptr;
}
#ifndef NDEBUG
void print(raw_ostream &OS) const {
for (const auto &PassPtr : Passes)
OS << PassPtr->getName() << "\n";
}
LLVM_DUMP_METHOD void dump() const;
#endif
};

} // namespace llvm::sandboxir

#endif // LLVM_SANDBOXIR_PASSMANAGER_H
35 changes: 31 additions & 4 deletions llvm/include/llvm/SandboxIR/SandboxIR.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class ConstantFP;
class ConstantAggregateZero;
class ConstantPointerNull;
class PoisonValue;
class BlockAddress;
class Context;
class Function;
class Instruction;
Expand Down Expand Up @@ -323,6 +324,7 @@ class Value {
friend class ConstantPointerNull; // For `Val`.
friend class UndefValue; // For `Val`.
friend class PoisonValue; // For `Val`.
friend class BlockAddress; // For `Val`.

/// All values point to the context.
Context &Ctx;
Expand Down Expand Up @@ -1112,6 +1114,33 @@ class PoisonValue final : public UndefValue {
#endif
};

class BlockAddress final : public Constant {
BlockAddress(llvm::BlockAddress *C, Context &Ctx)
: Constant(ClassID::BlockAddress, C, Ctx) {}
friend class Context; // For constructor.

public:
/// Return a BlockAddress for the specified function and basic block.
static BlockAddress *get(Function *F, BasicBlock *BB);

/// Return a BlockAddress for the specified basic block. The basic
/// block must be embedded into a function.
static BlockAddress *get(BasicBlock *BB);

/// Lookup an existing \c BlockAddress constant for the given BasicBlock.
///
/// \returns 0 if \c !BB->hasAddressTaken(), otherwise the \c BlockAddress.
static BlockAddress *lookup(const BasicBlock *BB);

Function *getFunction() const;
BasicBlock *getBasicBlock() const;

/// For isa/dyn_cast.
static bool classof(const sandboxir::Value *From) {
return From->getSubclassID() == ClassID::BlockAddress;
}
};

/// Iterator for `Instruction`s in a `BasicBlock.
/// \Returns an sandboxir::Instruction & when derereferenced.
class BBIterator {
Expand Down Expand Up @@ -1194,9 +1223,7 @@ class BasicBlock : public Value {
Instruction &back() const;

#ifndef NDEBUG
void verify() const final {
assert(isa<llvm::BasicBlock>(Val) && "Expected BasicBlock!");
}
void verify() const final;
void dumpOS(raw_ostream &OS) const final;
#endif
};
Expand Down Expand Up @@ -1435,7 +1462,7 @@ template <typename LLVMT> class SingleLLVMInstructionImpl : public Instruction {
#endif
};

class FenceInst : public SingleLLVMInstructionImpl<llvm::SelectInst> {
class FenceInst : public SingleLLVMInstructionImpl<llvm::FenceInst> {
FenceInst(llvm::FenceInst *FI, Context &Ctx)
: SingleLLVMInstructionImpl(ClassID::Fence, Opcode::Fence, FI, Ctx) {}
friend Context; // For constructor;
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/SandboxIR/SandboxIRValues.def
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ DEF_CONST(ConstantAggregateZero, ConstantAggregateZero)
DEF_CONST(ConstantPointerNull, ConstantPointerNull)
DEF_CONST(UndefValue, UndefValue)
DEF_CONST(PoisonValue, PoisonValue)
DEF_CONST(BlockAddress, BlockAddress)

#ifndef DEF_INSTR
#define DEF_INSTR(ID, OPCODE, CLASS)
Expand Down
46 changes: 46 additions & 0 deletions llvm/include/llvm/SandboxIR/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Context;
// Forward declare friend classes for MSVC.
class PointerType;
class VectorType;
class FixedVectorType;
class IntegerType;
class FunctionType;
class ArrayType;
Expand All @@ -41,6 +42,7 @@ class Type {
friend class ArrayType; // For LLVMTy.
friend class StructType; // For LLVMTy.
friend class VectorType; // For LLVMTy.
friend class FixedVectorType; // For LLVMTy.
friend class PointerType; // For LLVMTy.
friend class FunctionType; // For LLVMTy.
friend class IntegerType; // For LLVMTy.
Expand Down Expand Up @@ -344,6 +346,50 @@ class VectorType : public Type {
}
};

class FixedVectorType : public VectorType {
public:
static FixedVectorType *get(Type *ElementType, unsigned NumElts);

static FixedVectorType *get(Type *ElementType, const FixedVectorType *FVTy) {
return get(ElementType, FVTy->getNumElements());
}

static FixedVectorType *getInteger(FixedVectorType *VTy) {
return cast<FixedVectorType>(VectorType::getInteger(VTy));
}

static FixedVectorType *getExtendedElementVectorType(FixedVectorType *VTy) {
return cast<FixedVectorType>(VectorType::getExtendedElementVectorType(VTy));
}

static FixedVectorType *getTruncatedElementVectorType(FixedVectorType *VTy) {
return cast<FixedVectorType>(
VectorType::getTruncatedElementVectorType(VTy));
}

static FixedVectorType *getSubdividedVectorType(FixedVectorType *VTy,
int NumSubdivs) {
return cast<FixedVectorType>(
VectorType::getSubdividedVectorType(VTy, NumSubdivs));
}

static FixedVectorType *getHalfElementsVectorType(FixedVectorType *VTy) {
return cast<FixedVectorType>(VectorType::getHalfElementsVectorType(VTy));
}

static FixedVectorType *getDoubleElementsVectorType(FixedVectorType *VTy) {
return cast<FixedVectorType>(VectorType::getDoubleElementsVectorType(VTy));
}

static bool classof(const Type *T) {
return isa<llvm::FixedVectorType>(T->LLVMTy);
}

unsigned getNumElements() const {
return cast<llvm::FixedVectorType>(LLVMTy)->getNumElements();
}
};

class FunctionType : public Type {
public:
// TODO: add missing functions
Expand Down
7 changes: 4 additions & 3 deletions llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3496,7 +3496,6 @@ void DAGTypeLegalizer::ExpandIntRes_ADDSUB(SDNode *N,

if (N->getOpcode() == ISD::ADD) {
Lo = DAG.getNode(ISD::ADD, dl, NVT, LoOps);
Hi = DAG.getNode(ISD::ADD, dl, NVT, ArrayRef(HiOps, 2));
SDValue Cmp;
// Special case: X+1 has a carry out if X+1==0. This may reduce the live
// range of X. We assume comparing with 0 is cheap.
Expand All @@ -3521,10 +3520,12 @@ void DAGTypeLegalizer::ExpandIntRes_ADDSUB(SDNode *N,
Carry = DAG.getSelect(dl, NVT, Cmp, DAG.getConstant(1, dl, NVT),
DAG.getConstant(0, dl, NVT));

if (isAllOnesConstant(LoOps[1]) && isAllOnesConstant(HiOps[1]))
if (isAllOnesConstant(LoOps[1]) && isAllOnesConstant(HiOps[1])) {
Hi = DAG.getNode(ISD::SUB, dl, NVT, HiOps[0], Carry);
else
} else {
Hi = DAG.getNode(ISD::ADD, dl, NVT, ArrayRef(HiOps, 2));
Hi = DAG.getNode(ISD::ADD, dl, NVT, Hi, Carry);
}
} else {
Lo = DAG.getNode(ISD::SUB, dl, NVT, LoOps);
Hi = DAG.getNode(ISD::SUB, dl, NVT, ArrayRef(HiOps, 2));
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/SandboxIR/Pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "llvm/SandboxIR/Pass.h"
#include "llvm/SandboxIR/PassManager.h"
#include "llvm/Support/Debug.h"

using namespace llvm::sandboxir;
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/SandboxIR/PassManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ bool FunctionPassManager::runOnFunction(Function &F) {
// TODO: Check ChangeAll against hashes before/after.
return Change;
}
#ifndef NDEBUG
void PassRegistry::dump() const {
print(dbgs());
dbgs() << "\n";
}
#endif // NDEBUG
42 changes: 40 additions & 2 deletions llvm/lib/SandboxIR/SandboxIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2489,6 +2489,32 @@ PoisonValue *PoisonValue::getElementValue(unsigned Idx) const {
cast<llvm::PoisonValue>(Val)->getElementValue(Idx)));
}

BlockAddress *BlockAddress::get(Function *F, BasicBlock *BB) {
auto *LLVMC = llvm::BlockAddress::get(cast<llvm::Function>(F->Val),
cast<llvm::BasicBlock>(BB->Val));
return cast<BlockAddress>(F->getContext().getOrCreateConstant(LLVMC));
}

BlockAddress *BlockAddress::get(BasicBlock *BB) {
auto *LLVMC = llvm::BlockAddress::get(cast<llvm::BasicBlock>(BB->Val));
return cast<BlockAddress>(BB->getContext().getOrCreateConstant(LLVMC));
}

BlockAddress *BlockAddress::lookup(const BasicBlock *BB) {
auto *LLVMC = llvm::BlockAddress::lookup(cast<llvm::BasicBlock>(BB->Val));
return cast_or_null<BlockAddress>(BB->getContext().getValue(LLVMC));
}

Function *BlockAddress::getFunction() const {
return cast<Function>(
Ctx.getValue(cast<llvm::BlockAddress>(Val)->getFunction()));
}

BasicBlock *BlockAddress::getBasicBlock() const {
return cast<BasicBlock>(
Ctx.getValue(cast<llvm::BlockAddress>(Val)->getBasicBlock()));
}

FunctionType *Function::getFunctionType() const {
return cast<FunctionType>(
Ctx.getType(cast<llvm::Function>(Val)->getFunctionType()));
Expand Down Expand Up @@ -2585,6 +2611,10 @@ Value *Context::getOrCreateValueInternal(llvm::Value *LLVMV, llvm::User *U) {
It->second = std::unique_ptr<ConstantFP>(
new ConstantFP(cast<llvm::ConstantFP>(C), *this));
return It->second.get();
case llvm::Value::BlockAddressVal:
It->second = std::unique_ptr<BlockAddress>(
new BlockAddress(cast<llvm::BlockAddress>(C), *this));
return It->second.get();
case llvm::Value::ConstantAggregateZeroVal: {
auto *CAZ = cast<llvm::ConstantAggregateZero>(C);
It->second = std::unique_ptr<ConstantAggregateZero>(
Expand Down Expand Up @@ -2640,7 +2670,7 @@ Value *Context::getOrCreateValueInternal(llvm::Value *LLVMV, llvm::User *U) {
return It->second.get();
}
if (auto *BB = dyn_cast<llvm::BasicBlock>(LLVMV)) {
assert(isa<BlockAddress>(U) &&
assert(isa<llvm::BlockAddress>(U) &&
"This won't create a SBBB, don't call this function directly!");
if (auto *SBBB = getValue(BB))
return SBBB;
Expand Down Expand Up @@ -3173,7 +3203,7 @@ void BasicBlock::buildBasicBlockFromLLVMIR(llvm::BasicBlock *LLVMBB) {
Ctx.getOrCreateValue(Op);
}
}
#if !defined(NDEBUG) && defined(SBVEC_EXPENSIVE_CHECKS)
#if !defined(NDEBUG)
verify();
#endif
}
Expand Down Expand Up @@ -3249,4 +3279,12 @@ void BasicBlock::dumpOS(raw_ostream &OS) const {
}
}
}

void BasicBlock::verify() const {
assert(isa<llvm::BasicBlock>(Val) && "Expected BasicBlock!");
for (const auto &I : *this) {
I.verify();
}
}

#endif // NDEBUG
5 changes: 5 additions & 0 deletions llvm/lib/SandboxIR/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ bool VectorType::isValidElementType(Type *ElemTy) {
return llvm::VectorType::isValidElementType(ElemTy->LLVMTy);
}

FixedVectorType *FixedVectorType::get(Type *ElementType, unsigned NumElts) {
return cast<FixedVectorType>(ElementType->getContext().getType(
llvm::FixedVectorType::get(ElementType->LLVMTy, NumElts)));
}

IntegerType *IntegerType::get(Context &Ctx, unsigned NumBits) {
return cast<IntegerType>(
Ctx.getType(llvm::IntegerType::get(Ctx.LLVMCtx, NumBits)));
Expand Down
92 changes: 55 additions & 37 deletions llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ bool WebAssemblyAsmTypeCheck::popRefType(SMLoc ErrorLoc) {
return false;
}

bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCInst &Inst,
bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCOperand &LocalOp,
wasm::ValType &Type) {
auto Local = static_cast<size_t>(Inst.getOperand(0).getImm());
auto Local = static_cast<size_t>(LocalOp.getImm());
if (Local >= LocalTypes.size())
return typeError(ErrorLoc, StringRef("no local type specified for index ") +
std::to_string(Local));
Expand Down Expand Up @@ -178,21 +178,21 @@ bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc,
return false;
}

bool WebAssemblyAsmTypeCheck::getSymRef(SMLoc ErrorLoc, const MCInst &Inst,
bool WebAssemblyAsmTypeCheck::getSymRef(SMLoc ErrorLoc, const MCOperand &SymOp,
const MCSymbolRefExpr *&SymRef) {
auto Op = Inst.getOperand(0);
if (!Op.isExpr())
if (!SymOp.isExpr())
return typeError(ErrorLoc, StringRef("expected expression operand"));
SymRef = dyn_cast<MCSymbolRefExpr>(Op.getExpr());
SymRef = dyn_cast<MCSymbolRefExpr>(SymOp.getExpr());
if (!SymRef)
return typeError(ErrorLoc, StringRef("expected symbol operand"));
return false;
}

bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst,
bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc,
const MCOperand &GlobalOp,
wasm::ValType &Type) {
const MCSymbolRefExpr *SymRef;
if (getSymRef(ErrorLoc, Inst, SymRef))
if (getSymRef(ErrorLoc, GlobalOp, SymRef))
return true;
auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
switch (WasmSym->getType().value_or(wasm::WASM_SYMBOL_TYPE_DATA)) {
Expand All @@ -217,10 +217,10 @@ bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst,
return false;
}

bool WebAssemblyAsmTypeCheck::getTable(SMLoc ErrorLoc, const MCInst &Inst,
bool WebAssemblyAsmTypeCheck::getTable(SMLoc ErrorLoc, const MCOperand &TableOp,
wasm::ValType &Type) {
const MCSymbolRefExpr *SymRef;
if (getSymRef(ErrorLoc, Inst, SymRef))
if (getSymRef(ErrorLoc, TableOp, SymRef))
return true;
auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
if (WasmSym->getType().value_or(wasm::WASM_SYMBOL_TYPE_DATA) !=
Expand All @@ -231,6 +231,34 @@ bool WebAssemblyAsmTypeCheck::getTable(SMLoc ErrorLoc, const MCInst &Inst,
return false;
}

bool WebAssemblyAsmTypeCheck::getSignature(SMLoc ErrorLoc,
const MCOperand &SigOp,
wasm::WasmSymbolType Type,
const wasm::WasmSignature *&Sig) {
const MCSymbolRefExpr *SymRef = nullptr;
if (getSymRef(ErrorLoc, SigOp, SymRef))
return true;
const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
Sig = WasmSym->getSignature();

if (!Sig || WasmSym->getType() != Type) {
const char *TypeName = nullptr;
switch (Type) {
case wasm::WASM_SYMBOL_TYPE_FUNCTION:
TypeName = "func";
break;
case wasm::WASM_SYMBOL_TYPE_TAG:
TypeName = "tag";
break;
default:
return true;
}
return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
": missing ." + TypeName + "type");
}
return false;
}

bool WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc) {
// Check the return types.
for (auto RVT : llvm::reverse(ReturnTypes)) {
Expand All @@ -252,56 +280,56 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
dumpTypeStack("typechecking " + Name + ": ");
wasm::ValType Type;
if (Name == "local.get") {
if (getLocal(Operands[1]->getStartLoc(), Inst, Type))
if (getLocal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
return true;
Stack.push_back(Type);
} else if (Name == "local.set") {
if (getLocal(Operands[1]->getStartLoc(), Inst, Type))
if (getLocal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
return true;
if (popType(ErrorLoc, Type))
return true;
} else if (Name == "local.tee") {
if (getLocal(Operands[1]->getStartLoc(), Inst, Type))
if (getLocal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
return true;
if (popType(ErrorLoc, Type))
return true;
Stack.push_back(Type);
} else if (Name == "global.get") {
if (getGlobal(Operands[1]->getStartLoc(), Inst, Type))
if (getGlobal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
return true;
Stack.push_back(Type);
} else if (Name == "global.set") {
if (getGlobal(Operands[1]->getStartLoc(), Inst, Type))
if (getGlobal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
return true;
if (popType(ErrorLoc, Type))
return true;
} else if (Name == "table.get") {
if (getTable(Operands[1]->getStartLoc(), Inst, Type))
if (getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
return true;
if (popType(ErrorLoc, wasm::ValType::I32))
return true;
Stack.push_back(Type);
} else if (Name == "table.set") {
if (getTable(Operands[1]->getStartLoc(), Inst, Type))
if (getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
return true;
if (popType(ErrorLoc, Type))
return true;
if (popType(ErrorLoc, wasm::ValType::I32))
return true;
} else if (Name == "table.size") {
if (getTable(Operands[1]->getStartLoc(), Inst, Type))
if (getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
return true;
Stack.push_back(wasm::ValType::I32);
} else if (Name == "table.grow") {
if (getTable(Operands[1]->getStartLoc(), Inst, Type))
if (getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
return true;
if (popType(ErrorLoc, wasm::ValType::I32))
return true;
if (popType(ErrorLoc, Type))
return true;
Stack.push_back(wasm::ValType::I32);
} else if (Name == "table.fill") {
if (getTable(Operands[1]->getStartLoc(), Inst, Type))
if (getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
return true;
if (popType(ErrorLoc, wasm::ValType::I32))
return true;
Expand Down Expand Up @@ -352,15 +380,10 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
return true;
Unreachable = false;
if (Name == "catch") {
const MCSymbolRefExpr *SymRef;
if (getSymRef(Operands[1]->getStartLoc(), Inst, SymRef))
const wasm::WasmSignature *Sig = nullptr;
if (getSignature(Operands[1]->getStartLoc(), Inst.getOperand(0),
wasm::WASM_SYMBOL_TYPE_TAG, Sig))
return true;
const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
const auto *Sig = WasmSym->getSignature();
if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_TAG)
return typeError(Operands[1]->getStartLoc(), StringRef("symbol ") +
WasmSym->getName() +
": missing .tagtype");
// catch instruction pushes values whose types are specified in the tag's
// "params" part
Stack.insert(Stack.end(), Sig->Params.begin(), Sig->Params.end());
Expand All @@ -383,15 +406,10 @@ bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
if (Name == "return_call_indirect" && endOfFunction(ErrorLoc))
return true;
} else if (Name == "call" || Name == "return_call") {
const MCSymbolRefExpr *SymRef;
if (getSymRef(Operands[1]->getStartLoc(), Inst, SymRef))
return true;
auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
auto Sig = WasmSym->getSignature();
if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_FUNCTION)
return typeError(Operands[1]->getStartLoc(), StringRef("symbol ") +
WasmSym->getName() +
": missing .functype");
const wasm::WasmSignature *Sig = nullptr;
if (getSignature(Operands[1]->getStartLoc(), Inst.getOperand(0),
wasm::WASM_SYMBOL_TYPE_FUNCTION, Sig))
return true;
if (checkSig(ErrorLoc, *Sig))
return true;
if (Name == "return_call" && endOfFunction(ErrorLoc))
Expand Down
11 changes: 7 additions & 4 deletions llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,17 @@ class WebAssemblyAsmTypeCheck final {
bool typeError(SMLoc ErrorLoc, const Twine &Msg);
bool popType(SMLoc ErrorLoc, std::optional<wasm::ValType> EVT);
bool popRefType(SMLoc ErrorLoc);
bool getLocal(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type);
bool getLocal(SMLoc ErrorLoc, const MCOperand &LocalOp, wasm::ValType &Type);
bool checkEnd(SMLoc ErrorLoc, bool PopVals = false);
bool checkBr(SMLoc ErrorLoc, size_t Level);
bool checkSig(SMLoc ErrorLoc, const wasm::WasmSignature &Sig);
bool getSymRef(SMLoc ErrorLoc, const MCInst &Inst,
bool getSymRef(SMLoc ErrorLoc, const MCOperand &SymOp,
const MCSymbolRefExpr *&SymRef);
bool getGlobal(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type);
bool getTable(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type);
bool getGlobal(SMLoc ErrorLoc, const MCOperand &GlobalOp,
wasm::ValType &Type);
bool getTable(SMLoc ErrorLoc, const MCOperand &TableOp, wasm::ValType &Type);
bool getSignature(SMLoc ErrorLoc, const MCOperand &SigOp,
wasm::WasmSymbolType Type, const wasm::WasmSignature *&Sig);

public:
WebAssemblyAsmTypeCheck(MCAsmParser &Parser, const MCInstrInfo &MII,
Expand Down
42 changes: 26 additions & 16 deletions llvm/lib/Transforms/IPO/MemProfContextDisambiguation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,8 +470,20 @@ class CallsiteContextGraph {
private:
using EdgeIter = typename std::vector<std::shared_ptr<ContextEdge>>::iterator;

using CallContextInfo = std::tuple<CallTy, std::vector<uint64_t>,
const FuncTy *, DenseSet<uint32_t>>;
// Structure to keep track of information for each call as we are matching
// non-allocation callsites onto context nodes created from the allocation
// call metadata / summary contexts.
struct CallContextInfo {
// The callsite we're trying to match.
CallTy Call;
// The callsites stack ids that have a context node in the graph.
std::vector<uint64_t> StackIds;
// The function containing this callsite.
const FuncTy *Func;
// Initially empty, if needed this will be updated to contain the context
// ids for use in a new context node created for this callsite.
DenseSet<uint32_t> ContextIds;
};

/// Assigns the given Node to calls at or inlined into the location with
/// the Node's stack id, after post order traversing and processing its
Expand Down Expand Up @@ -1458,7 +1470,7 @@ void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::updateStackNodes() {
auto &Calls = It.getSecond();
// Skip single calls with a single stack id. These don't need a new node.
if (Calls.size() == 1) {
auto &Ids = std::get<1>(Calls[0]);
auto &Ids = Calls[0].StackIds;
if (Ids.size() == 1)
continue;
}
Expand All @@ -1474,18 +1486,15 @@ void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::updateStackNodes() {
// that to sort by.
DenseMap<const FuncTy *, unsigned> FuncToIndex;
for (const auto &[Idx, CallCtxInfo] : enumerate(Calls))
FuncToIndex.insert({std::get<2>(CallCtxInfo), Idx});
FuncToIndex.insert({CallCtxInfo.Func, Idx});
std::stable_sort(
Calls.begin(), Calls.end(),
[&FuncToIndex](const CallContextInfo &A, const CallContextInfo &B) {
auto &IdsA = std::get<1>(A);
auto &IdsB = std::get<1>(B);
auto *FuncA = std::get<2>(A);
auto *FuncB = std::get<2>(B);
return IdsA.size() > IdsB.size() ||
(IdsA.size() == IdsB.size() &&
(IdsA < IdsB ||
(IdsA == IdsB && FuncToIndex[FuncA] < FuncToIndex[FuncB])));
return A.StackIds.size() > B.StackIds.size() ||
(A.StackIds.size() == B.StackIds.size() &&
(A.StackIds < B.StackIds ||
(A.StackIds == B.StackIds &&
FuncToIndex[A.Func] < FuncToIndex[B.Func])));
});

// Find the node for the last stack id, which should be the same
Expand Down Expand Up @@ -1520,7 +1529,7 @@ void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::updateStackNodes() {
#ifndef NDEBUG
// If this call has a different set of ids than the last one, clear the
// set used to ensure they are sorted properly.
if (I > 0 && Ids != std::get<1>(Calls[I - 1]))
if (I > 0 && Ids != Calls[I - 1].StackIds)
MatchingIdsFuncSet.clear();
else
// If the prior call had the same stack ids this set would not be empty.
Expand Down Expand Up @@ -1607,17 +1616,18 @@ void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::updateStackNodes() {
// assigned to the same context node, and skip them.
bool DuplicateContextIds = false;
for (unsigned J = I + 1; J < Calls.size(); J++) {
auto &NextIds = std::get<1>(Calls[J]);
auto &CallCtxInfo = Calls[J];
auto &NextIds = CallCtxInfo.StackIds;
if (NextIds != Ids)
break;
auto *NextFunc = std::get<2>(Calls[J]);
auto *NextFunc = CallCtxInfo.Func;
if (NextFunc != Func) {
// We have another Call with the same ids but that cannot share this
// node, must duplicate ids for it.
DuplicateContextIds = true;
break;
}
auto &NextCall = std::get<0>(Calls[J]);
auto &NextCall = CallCtxInfo.Call;
CallToMatchingCall[NextCall] = Call;
// Update I so that it gets incremented correctly to skip this call.
I = J;
Expand Down
85 changes: 85 additions & 0 deletions llvm/test/CodeGen/RISCV/rv64zba.ll
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,60 @@ define i64 @addmul72(i64 %a, i64 %b) {
ret i64 %d
}

define i64 @mul50(i64 %a) {
; CHECK-LABEL: mul50:
; CHECK: # %bb.0:
; CHECK-NEXT: li a1, 50
; CHECK-NEXT: mul a0, a0, a1
; CHECK-NEXT: ret
%c = mul i64 %a, 50
ret i64 %c
}

define i64 @addmul50(i64 %a, i64 %b) {
; CHECK-LABEL: addmul50:
; CHECK: # %bb.0:
; CHECK-NEXT: li a2, 50
; CHECK-NEXT: mul a0, a0, a2
; CHECK-NEXT: add a0, a0, a1
; CHECK-NEXT: ret
%c = mul i64 %a, 50
%d = add i64 %c, %b
ret i64 %d
}

define i64 @mul100(i64 %a) {
; CHECK-LABEL: mul100:
; CHECK: # %bb.0:
; CHECK-NEXT: li a1, 100
; CHECK-NEXT: mul a0, a0, a1
; CHECK-NEXT: ret
%c = mul i64 %a, 100
ret i64 %c
}

define i64 @addmul100(i64 %a, i64 %b) {
; CHECK-LABEL: addmul100:
; CHECK: # %bb.0:
; CHECK-NEXT: li a2, 100
; CHECK-NEXT: mul a0, a0, a2
; CHECK-NEXT: add a0, a0, a1
; CHECK-NEXT: ret
%c = mul i64 %a, 100
%d = add i64 %c, %b
ret i64 %d
}

define i64 @mul162(i64 %a) {
; CHECK-LABEL: mul162:
; CHECK: # %bb.0:
; CHECK-NEXT: li a1, 162
; CHECK-NEXT: mul a0, a0, a1
; CHECK-NEXT: ret
%c = mul i64 %a, 162
ret i64 %c
}

define i64 @addmul162(i64 %a, i64 %b) {
; CHECK-LABEL: addmul162:
; CHECK: # %bb.0:
Expand All @@ -581,6 +635,16 @@ define i64 @addmul162(i64 %a, i64 %b) {
ret i64 %d
}

define i64 @mul180(i64 %a) {
; CHECK-LABEL: mul180:
; CHECK: # %bb.0:
; CHECK-NEXT: li a1, 180
; CHECK-NEXT: mul a0, a0, a1
; CHECK-NEXT: ret
%c = mul i64 %a, 180
ret i64 %c
}

define i64 @addmul180(i64 %a, i64 %b) {
; CHECK-LABEL: addmul180:
; CHECK: # %bb.0:
Expand All @@ -605,6 +669,27 @@ define i64 @add255mul180(i64 %a) {
ret i64 %d
}

define i64 @mul200(i64 %a) {
; CHECK-LABEL: mul200:
; CHECK: # %bb.0:
; CHECK-NEXT: li a1, 200
; CHECK-NEXT: mul a0, a0, a1
; CHECK-NEXT: ret
%c = mul i64 %a, 200
ret i64 %c
}

define i64 @addmul200(i64 %a, i64 %b) {
; CHECK-LABEL: addmul200:
; CHECK: # %bb.0:
; CHECK-NEXT: li a2, 200
; CHECK-NEXT: mul a0, a0, a2
; CHECK-NEXT: add a0, a0, a1
; CHECK-NEXT: ret
%c = mul i64 %a, 200
%d = add i64 %c, %b
ret i64 %d
}

define i64 @addmul4096(i64 %a, i64 %b) {
; CHECK-LABEL: addmul4096:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ define i64 @i64_test(i64 %i) nounwind readnone {
; CHECK-NEXT: t24: i32 = ADD_R t5, t22, TargetConstant:i32<0>
; CHECK-NEXT: t3: i32,ch = LDW_RI<Mem:(load (s32) from %fixed-stack.1, align 8)> TargetFrameIndex:i32<-1>, TargetConstant:i32<0>, TargetConstant:i32<0>, t0
; CHECK-NEXT: t19: i32,ch = LDW_RI<Mem:(dereferenceable load (s32) from %ir.loc, align 8)> TargetFrameIndex:i32<0>, TargetConstant:i32<0>, TargetConstant:i32<0>, t0
; CHECK-NEXT: t25: i32 = ADD_R t3, t19, TargetConstant:i32<0>
; CHECK-NEXT: t27: i32 = ADD_R t3, t19, TargetConstant:i32<0>
; CHECK-NEXT: t30: i32,glue = SFSUB_F_RR t24, t5
; CHECK-NEXT: t31: i32 = SCC TargetConstant:i32<4>, t30:1
; CHECK-NEXT: t28: i32 = ADD_R t25, t31, TargetConstant:i32<0>
; CHECK-NEXT: t28: i32 = ADD_R t27, t31, TargetConstant:i32<0>
; CHECK-NEXT: t15: ch,glue = CopyToReg t0, Register:i32 $rv, t28
; CHECK-NEXT: t17: ch,glue = CopyToReg t15, Register:i32 $r9, t24, t15:1
; CHECK-NEXT: t18: ch = RET Register:i32 $rv, Register:i32 $r9, t17, t17:1
Expand Down
29 changes: 29 additions & 0 deletions llvm/unittests/SandboxIR/PassTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,32 @@ define void @foo() {
EXPECT_EQ(Buff, "test-fpm(test-pass1,test-pass2)");
#endif // NDEBUG
}

TEST_F(PassTest, PassRegistry) {
class TestPass1 final : public FunctionPass {
public:
TestPass1() : FunctionPass("test-pass1") {}
bool runOnFunction(Function &F) final { return false; }
};
class TestPass2 final : public FunctionPass {
public:
TestPass2() : FunctionPass("test-pass2") {}
bool runOnFunction(Function &F) final { return false; }
};

PassRegistry Registry;
auto &TP1 = Registry.registerPass(std::make_unique<TestPass1>());
auto &TP2 = Registry.registerPass(std::make_unique<TestPass2>());

// Check getPassByName().
EXPECT_EQ(Registry.getPassByName("test-pass1"), &TP1);
EXPECT_EQ(Registry.getPassByName("test-pass2"), &TP2);

#ifndef NDEBUG
// Check print().
std::string Buff;
llvm::raw_string_ostream SS(Buff);
Registry.print(SS);
EXPECT_EQ(Buff, "test-pass1\ntest-pass2\n");
#endif // NDEBUG
}
48 changes: 48 additions & 0 deletions llvm/unittests/SandboxIR/SandboxIRTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,54 @@ define void @foo() {
EXPECT_EQ(UndefStruct->getNumElements(), 2u);
}

TEST_F(SandboxIRTest, BlockAddress) {
parseIR(C, R"IR(
define void @foo(ptr %ptr) {
bb0:
store ptr blockaddress(@foo, %bb0), ptr %ptr
ret void
bb1:
ret void
bb2:
ret void
}
)IR");
Function &LLVMF = *M->getFunction("foo");
sandboxir::Context Ctx(C);

auto &F = *Ctx.createFunction(&LLVMF);
auto *BB0 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb0")));
auto *BB1 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb1")));
auto *BB2 = cast<sandboxir::BasicBlock>(
Ctx.getValue(getBasicBlockByName(LLVMF, "bb2")));
auto It = BB0->begin();
auto *SI = cast<sandboxir::StoreInst>(&*It++);
[[maybe_unused]] auto *Ret = cast<sandboxir::ReturnInst>(&*It++);

// Check classof(), creation, getFunction(), getBasicBlock().
auto *BB0Addr = cast<sandboxir::BlockAddress>(SI->getValueOperand());
EXPECT_EQ(BB0Addr->getBasicBlock(), BB0);
EXPECT_EQ(BB0Addr->getFunction(), &F);
// Check get(F, BB).
auto *NewBB0Addr = sandboxir::BlockAddress::get(&F, BB0);
EXPECT_EQ(NewBB0Addr, BB0Addr);
// Check get(BB).
auto *NewBB0Addr2 = sandboxir::BlockAddress::get(BB0);
EXPECT_EQ(NewBB0Addr2, BB0Addr);
auto *BB1Addr = sandboxir::BlockAddress::get(BB1);
EXPECT_EQ(BB1Addr->getBasicBlock(), BB1);
EXPECT_NE(BB1Addr, BB0Addr);
// Check lookup().
auto *LookupBB0Addr = sandboxir::BlockAddress::lookup(BB0);
EXPECT_EQ(LookupBB0Addr, BB0Addr);
auto *LookupBB1Addr = sandboxir::BlockAddress::lookup(BB1);
EXPECT_EQ(LookupBB1Addr, BB1Addr);
auto *LookupBB2Addr = sandboxir::BlockAddress::lookup(BB2);
EXPECT_EQ(LookupBB2Addr, nullptr);
}

TEST_F(SandboxIRTest, Use) {
parseIR(C, R"IR(
define i32 @foo(i32 %v0, i32 %v1) {
Expand Down
58 changes: 58 additions & 0 deletions llvm/unittests/SandboxIR/TypesTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,64 @@ define void @foo(<4 x i16> %vi0, <4 x float> %vf1, i8 %i0) {
EXPECT_FALSE(sandboxir::VectorType::isValidElementType(FVecTy));
}

TEST_F(SandboxTypeTest, FixedVectorType) {
parseIR(C, R"IR(
define void @foo(<4 x i16> %vi0, <4 x float> %vf1, i8 %i0) {
ret void
}
)IR");
llvm::Function *LLVMF = &*M->getFunction("foo");
sandboxir::Context Ctx(C);
auto *F = Ctx.createFunction(LLVMF);
// Check classof(), creation, accessors
auto *Vec4i16Ty = cast<sandboxir::FixedVectorType>(F->getArg(0)->getType());
EXPECT_TRUE(Vec4i16Ty->getElementType()->isIntegerTy(16));
EXPECT_EQ(Vec4i16Ty->getElementCount(), ElementCount::getFixed(4));

// get(ElementType, NumElements)
EXPECT_EQ(
sandboxir::FixedVectorType::get(sandboxir::Type::getInt16Ty(Ctx), 4),
F->getArg(0)->getType());
// get(ElementType, Other)
EXPECT_EQ(sandboxir::FixedVectorType::get(
sandboxir::Type::getInt16Ty(Ctx),
cast<sandboxir::FixedVectorType>(F->getArg(0)->getType())),
F->getArg(0)->getType());
auto *Vec4FTy = cast<sandboxir::FixedVectorType>(F->getArg(1)->getType());
EXPECT_TRUE(Vec4FTy->getElementType()->isFloatTy());
// getInteger
auto *Vec4i32Ty = sandboxir::FixedVectorType::getInteger(Vec4FTy);
EXPECT_TRUE(Vec4i32Ty->getElementType()->isIntegerTy(32));
EXPECT_EQ(Vec4i32Ty->getElementCount(), Vec4FTy->getElementCount());
// getExtendedElementCountVectorType
auto *Vec4i64Ty =
sandboxir::FixedVectorType::getExtendedElementVectorType(Vec4i16Ty);
EXPECT_TRUE(Vec4i64Ty->getElementType()->isIntegerTy(32));
EXPECT_EQ(Vec4i64Ty->getElementCount(), Vec4i16Ty->getElementCount());
// getTruncatedElementVectorType
auto *Vec4i8Ty =
sandboxir::FixedVectorType::getTruncatedElementVectorType(Vec4i16Ty);
EXPECT_TRUE(Vec4i8Ty->getElementType()->isIntegerTy(8));
EXPECT_EQ(Vec4i8Ty->getElementCount(), Vec4i8Ty->getElementCount());
// getSubdividedVectorType
auto *Vec8i8Ty =
sandboxir::FixedVectorType::getSubdividedVectorType(Vec4i16Ty, 1);
EXPECT_TRUE(Vec8i8Ty->getElementType()->isIntegerTy(8));
EXPECT_EQ(Vec8i8Ty->getElementCount(), ElementCount::getFixed(8));
// getNumElements
EXPECT_EQ(Vec8i8Ty->getNumElements(), 8u);
// getHalfElementsVectorType
auto *Vec2i16Ty =
sandboxir::FixedVectorType::getHalfElementsVectorType(Vec4i16Ty);
EXPECT_TRUE(Vec2i16Ty->getElementType()->isIntegerTy(16));
EXPECT_EQ(Vec2i16Ty->getElementCount(), ElementCount::getFixed(2));
// getDoubleElementsVectorType
auto *Vec8i16Ty =
sandboxir::FixedVectorType::getDoubleElementsVectorType(Vec4i16Ty);
EXPECT_TRUE(Vec8i16Ty->getElementType()->isIntegerTy(16));
EXPECT_EQ(Vec8i16Ty->getElementCount(), ElementCount::getFixed(8));
}

TEST_F(SandboxTypeTest, FunctionType) {
parseIR(C, R"IR(
define void @foo() {
Expand Down
28 changes: 2 additions & 26 deletions llvm/utils/lit/lit/TestRunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -1017,20 +1017,6 @@ def _executeShCmd(cmd, shenv, results, timeoutHelper):
return exitCode


def findColor(line, curr_color):
start = line.rfind("\33[")
if start == -1:
return curr_color
end = line.find("m", start+2)
if end == -1:
return curr_color
match = line[start:end+1]
# "\33[0m" means "reset all formatting". Sometimes the 0 is skipped.
if match == "\33[m" or match == "\33[0m":
return None
return match


def formatOutput(title, data, limit=None):
if not data.strip():
return ""
Expand All @@ -1041,18 +1027,8 @@ def formatOutput(title, data, limit=None):
msg = ""
ndashes = 30
# fmt: off
out = f"# .---{title}{'-' * (ndashes - 4 - len(title))}\n"
curr_color = None
for line in data.splitlines():
if curr_color:
out += "\33[0m"
out += "# | "
if curr_color:
out += curr_color
out += line + "\n"
curr_color = findColor(line, curr_color)
if curr_color:
out += "\33[0m" # prevent unterminated formatting from leaking
out = f"# .---{title}{'-' * (ndashes - 4 - len(title))}\n"
out += f"# | " + "\n# | ".join(data.splitlines()) + "\n"
out += f"# `---{msg}{'-' * (ndashes - 4 - len(msg))}\n"
# fmt: on
return out
Expand Down
10 changes: 0 additions & 10 deletions llvm/utils/lit/tests/Inputs/escape-color/color-escaped.txt

This file was deleted.

6 changes: 0 additions & 6 deletions llvm/utils/lit/tests/Inputs/escape-color/color.txt

This file was deleted.

8 changes: 0 additions & 8 deletions llvm/utils/lit/tests/Inputs/escape-color/lit.cfg

This file was deleted.

4 changes: 0 additions & 4 deletions llvm/utils/lit/tests/escape-color.py

This file was deleted.