734 changes: 0 additions & 734 deletions clang/lib/AST/Interp/ByteCodeStmtGen.cpp

This file was deleted.

91 changes: 0 additions & 91 deletions clang/lib/AST/Interp/ByteCodeStmtGen.h

This file was deleted.

1,002 changes: 837 additions & 165 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp → clang/lib/AST/Interp/Compiler.cpp

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- ByteCodeExprGen.h - Code generator for expressions -----*- C++ -*-===//
//===--- Compiler.h - Code generator for expressions -----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
Expand Down Expand Up @@ -36,8 +36,11 @@ template <class Emitter> class InitLinkScope;
template <class Emitter> class OptionScope;
template <class Emitter> class ArrayIndexScope;
template <class Emitter> class SourceLocScope;
template <class Emitter> class LoopScope;
template <class Emitter> class LabelScope;
template <class Emitter> class SwitchScope;

template <class Emitter> class ByteCodeExprGen;
template <class Emitter> class Compiler;
struct InitLink {
public:
enum {
Expand All @@ -60,7 +63,7 @@ struct InitLink {

InitLink(uint8_t Kind) : Kind(Kind) {}
template <class Emitter>
bool emit(ByteCodeExprGen<Emitter> *Ctx, const Expr *E) const;
bool emit(Compiler<Emitter> *Ctx, const Expr *E) const;

private:
uint32_t Kind;
Expand All @@ -84,12 +87,14 @@ struct VarCreationState {

/// Compilation context for expressions.
template <class Emitter>
class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
public Emitter {
class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
public Emitter {
protected:
// Aliases for types defined in the emitter.
using LabelTy = typename Emitter::LabelTy;
using AddrTy = typename Emitter::AddrTy;
using OptLabelTy = std::optional<LabelTy>;
using CaseMap = llvm::DenseMap<const SwitchCase *, LabelTy>;

/// Current compilation context.
Context &Ctx;
Expand All @@ -99,10 +104,10 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
public:
/// Initializes the compiler and the backend emitter.
template <typename... Tys>
ByteCodeExprGen(Context &Ctx, Program &P, Tys &&... Args)
Compiler(Context &Ctx, Program &P, Tys &&...Args)
: Emitter(Ctx, P, Args...), Ctx(Ctx), P(P) {}

// Expression visitors - result returned on interp stack.
// Expressions.
bool VisitCastExpr(const CastExpr *E);
bool VisitIntegerLiteral(const IntegerLiteral *E);
bool VisitFloatingLiteral(const FloatingLiteral *E);
Expand Down Expand Up @@ -179,9 +184,29 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E);
bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);

// Statements.
bool visitCompoundStmt(const CompoundStmt *S);
bool visitLoopBody(const Stmt *S);
bool visitDeclStmt(const DeclStmt *DS);
bool visitReturnStmt(const ReturnStmt *RS);
bool visitIfStmt(const IfStmt *IS);
bool visitWhileStmt(const WhileStmt *S);
bool visitDoStmt(const DoStmt *S);
bool visitForStmt(const ForStmt *S);
bool visitCXXForRangeStmt(const CXXForRangeStmt *S);
bool visitBreakStmt(const BreakStmt *S);
bool visitContinueStmt(const ContinueStmt *S);
bool visitSwitchStmt(const SwitchStmt *S);
bool visitCaseStmt(const CaseStmt *S);
bool visitDefaultStmt(const DefaultStmt *S);
bool visitAttributedStmt(const AttributedStmt *S);
bool visitCXXTryStmt(const CXXTryStmt *S);

protected:
bool visitStmt(const Stmt *S);
bool visitExpr(const Expr *E) override;
bool visitDecl(const VarDecl *VD, bool ConstantContext) override;
bool visitFunc(const FunctionDecl *F) override;

protected:
/// Emits scope cleanup instructions.
Expand All @@ -194,8 +219,8 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
Record *getRecord(QualType Ty);
Record *getRecord(const RecordDecl *RD);

// Returns a function for the given FunctionDecl.
// If the function does not exist yet, it is compiled.
/// Returns a function for the given FunctionDecl.
/// If the function does not exist yet, it is compiled.
const Function *getFunction(const FunctionDecl *FD);

std::optional<PrimType> classify(const Expr *E) const {
Expand Down Expand Up @@ -305,6 +330,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
friend class ArrayIndexScope<Emitter>;
friend class SourceLocScope<Emitter>;
friend struct InitLink;
friend class LoopScope<Emitter>;
friend class LabelScope<Emitter>;
friend class SwitchScope<Emitter>;

/// Emits a zero initializer.
bool visitZeroInitializer(PrimType T, QualType QT, const Expr *E);
Expand Down Expand Up @@ -348,6 +376,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
bool emitDestruction(const Descriptor *Desc);
unsigned collectBaseOffset(const QualType BaseType,
const QualType DerivedType);
bool emitLambdaStaticInvokerBody(const CXXMethodDecl *MD);

protected:
/// Variable to storage mapping.
Expand Down Expand Up @@ -378,15 +407,28 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,

/// Flag indicating if we're initializing a global variable.
bool GlobalDecl = false;

/// Type of the expression returned by the function.
std::optional<PrimType> ReturnType;

/// Switch case mapping.
CaseMap CaseLabels;

/// Point to break to.
OptLabelTy BreakLabel;
/// Point to continue to.
OptLabelTy ContinueLabel;
/// Default case label.
OptLabelTy DefaultLabel;
};

extern template class ByteCodeExprGen<ByteCodeEmitter>;
extern template class ByteCodeExprGen<EvalEmitter>;
extern template class Compiler<ByteCodeEmitter>;
extern template class Compiler<EvalEmitter>;

/// Scope chain managing the variable lifetimes.
template <class Emitter> class VariableScope {
public:
VariableScope(ByteCodeExprGen<Emitter> *Ctx, const ValueDecl *VD)
VariableScope(Compiler<Emitter> *Ctx, const ValueDecl *VD)
: Ctx(Ctx), Parent(Ctx->VarScope), ValDecl(VD) {
Ctx->VarScope = this;
}
Expand Down Expand Up @@ -433,8 +475,8 @@ template <class Emitter> class VariableScope {
VariableScope *getParent() const { return Parent; }

protected:
/// ByteCodeExprGen instance.
ByteCodeExprGen<Emitter> *Ctx;
/// Compiler instance.
Compiler<Emitter> *Ctx;
/// Link to the parent scope.
VariableScope *Parent;
const ValueDecl *ValDecl = nullptr;
Expand All @@ -443,8 +485,7 @@ template <class Emitter> class VariableScope {
/// Generic scope for local variables.
template <class Emitter> class LocalScope : public VariableScope<Emitter> {
public:
LocalScope(ByteCodeExprGen<Emitter> *Ctx)
: VariableScope<Emitter>(Ctx, nullptr) {}
LocalScope(Compiler<Emitter> *Ctx) : VariableScope<Emitter>(Ctx, nullptr) {}

/// Emit a Destroy op for this scope.
~LocalScope() override {
Expand Down Expand Up @@ -538,8 +579,7 @@ template <class Emitter> class DestructorScope final {
/// variables are automatically emitted when the AutoScope is destroyed.
template <class Emitter> class AutoScope : public LocalScope<Emitter> {
public:
AutoScope(ByteCodeExprGen<Emitter> *Ctx)
: LocalScope<Emitter>(Ctx), DS(*this) {}
AutoScope(Compiler<Emitter> *Ctx) : LocalScope<Emitter>(Ctx), DS(*this) {}

private:
DestructorScope<Emitter> DS;
Expand All @@ -548,7 +588,7 @@ template <class Emitter> class AutoScope : public LocalScope<Emitter> {
/// Scope for storage declared in a compound statement.
template <class Emitter> class BlockScope final : public AutoScope<Emitter> {
public:
BlockScope(ByteCodeExprGen<Emitter> *Ctx) : AutoScope<Emitter>(Ctx) {}
BlockScope(Compiler<Emitter> *Ctx) : AutoScope<Emitter>(Ctx) {}

void addExtended(const Scope::Local &Local) override {
// If we to this point, just add the variable as a normal local
Expand All @@ -560,27 +600,26 @@ template <class Emitter> class BlockScope final : public AutoScope<Emitter> {

template <class Emitter> class ExprScope final : public AutoScope<Emitter> {
public:
ExprScope(ByteCodeExprGen<Emitter> *Ctx) : AutoScope<Emitter>(Ctx) {}
ExprScope(Compiler<Emitter> *Ctx) : AutoScope<Emitter>(Ctx) {}
};

template <class Emitter> class ArrayIndexScope final {
public:
ArrayIndexScope(ByteCodeExprGen<Emitter> *Ctx, uint64_t Index) : Ctx(Ctx) {
ArrayIndexScope(Compiler<Emitter> *Ctx, uint64_t Index) : Ctx(Ctx) {
OldArrayIndex = Ctx->ArrayIndex;
Ctx->ArrayIndex = Index;
}

~ArrayIndexScope() { Ctx->ArrayIndex = OldArrayIndex; }

private:
ByteCodeExprGen<Emitter> *Ctx;
Compiler<Emitter> *Ctx;
std::optional<uint64_t> OldArrayIndex;
};

template <class Emitter> class SourceLocScope final {
public:
SourceLocScope(ByteCodeExprGen<Emitter> *Ctx, const Expr *DefaultExpr)
: Ctx(Ctx) {
SourceLocScope(Compiler<Emitter> *Ctx, const Expr *DefaultExpr) : Ctx(Ctx) {
assert(DefaultExpr);
// We only switch if the current SourceLocDefaultExpr is null.
if (!Ctx->SourceLocDefaultExpr) {
Expand All @@ -595,20 +634,20 @@ template <class Emitter> class SourceLocScope final {
}

private:
ByteCodeExprGen<Emitter> *Ctx;
Compiler<Emitter> *Ctx;
bool Enabled = false;
};

template <class Emitter> class InitLinkScope final {
public:
InitLinkScope(ByteCodeExprGen<Emitter> *Ctx, InitLink &&Link) : Ctx(Ctx) {
InitLinkScope(Compiler<Emitter> *Ctx, InitLink &&Link) : Ctx(Ctx) {
Ctx->InitStack.push_back(std::move(Link));
}

~InitLinkScope() { this->Ctx->InitStack.pop_back(); }

private:
ByteCodeExprGen<Emitter> *Ctx;
Compiler<Emitter> *Ctx;
};

} // namespace interp
Expand Down
13 changes: 6 additions & 7 deletions clang/lib/AST/Interp/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@

#include "Context.h"
#include "ByteCodeEmitter.h"
#include "ByteCodeExprGen.h"
#include "ByteCodeStmtGen.h"
#include "Compiler.h"
#include "EvalEmitter.h"
#include "Interp.h"
#include "InterpFrame.h"
Expand All @@ -30,7 +29,7 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
assert(Stk.empty());
Function *Func = P->getFunction(FD);
if (!Func || !Func->hasBody())
Func = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD);
Func = Compiler<ByteCodeEmitter>(*this, *P).compileFunc(FD);

APValue DummyResult;
if (!Run(Parent, Func, DummyResult))
Expand All @@ -41,7 +40,7 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {

bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
bool Recursing = !Stk.empty();
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk);
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);

auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue());

Expand All @@ -67,7 +66,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {

bool Context::evaluate(State &Parent, const Expr *E, APValue &Result) {
bool Recursing = !Stk.empty();
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk);
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);

auto Res = C.interpretExpr(E);
if (Res.isInvalid()) {
Expand All @@ -92,7 +91,7 @@ bool Context::evaluate(State &Parent, const Expr *E, APValue &Result) {
bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
APValue &Result) {
bool Recursing = !Stk.empty();
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk);
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);

bool CheckGlobalInitialized =
shouldBeGloballyIndexed(VD) &&
Expand Down Expand Up @@ -261,7 +260,7 @@ const Function *Context::getOrCreateFunction(const FunctionDecl *FD) {
return Func;

if (!Func || WasNotDefined) {
if (auto F = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD))
if (auto F = Compiler<ByteCodeEmitter>(*this, *P).compileFunc(FD))
Func = F;
}

Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/EvalEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class EvalEmitter : public SourceMapper {
/// Methods implemented by the compiler.
virtual bool visitExpr(const Expr *E) = 0;
virtual bool visitDecl(const VarDecl *VD, bool ConstantContext) = 0;
virtual bool visitFunc(const FunctionDecl *F) = 0;

/// Emits jumps.
bool jumpTrue(const LabelTy &Label);
Expand Down
1 change: 0 additions & 1 deletion clang/lib/AST/Interp/Program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//

#include "Program.h"
#include "ByteCodeStmtGen.h"
#include "Context.h"
#include "Function.h"
#include "Integral.h"
Expand Down
40 changes: 14 additions & 26 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18632,28 +18632,6 @@ Value *CodeGenFunction::EmitAMDGPUBuiltinExpr(unsigned BuiltinID,
Function *F = CGM.getIntrinsic(Intrin, { Src0->getType() });
return Builder.CreateCall(F, { Src0, Builder.getFalse() });
}
case AMDGPU::BI__builtin_amdgcn_ds_fminf:
case AMDGPU::BI__builtin_amdgcn_ds_fmaxf: {
Intrinsic::ID Intrin;
switch (BuiltinID) {
case AMDGPU::BI__builtin_amdgcn_ds_fminf:
Intrin = Intrinsic::amdgcn_ds_fmin;
break;
case AMDGPU::BI__builtin_amdgcn_ds_fmaxf:
Intrin = Intrinsic::amdgcn_ds_fmax;
break;
}
llvm::Value *Src0 = EmitScalarExpr(E->getArg(0));
llvm::Value *Src1 = EmitScalarExpr(E->getArg(1));
llvm::Value *Src2 = EmitScalarExpr(E->getArg(2));
llvm::Value *Src3 = EmitScalarExpr(E->getArg(3));
llvm::Value *Src4 = EmitScalarExpr(E->getArg(4));
llvm::Function *F = CGM.getIntrinsic(Intrin, { Src1->getType() });
llvm::FunctionType *FTy = F->getFunctionType();
llvm::Type *PTy = FTy->getParamType(0);
Src0 = Builder.CreatePointerBitCastOrAddrSpaceCast(Src0, PTy);
return Builder.CreateCall(F, { Src0, Src1, Src2, Src3, Src4 });
}
case AMDGPU::BI__builtin_amdgcn_global_atomic_fadd_f64:
case AMDGPU::BI__builtin_amdgcn_global_atomic_fadd_f32:
case AMDGPU::BI__builtin_amdgcn_global_atomic_fadd_v2f16:
Expand Down Expand Up @@ -19087,11 +19065,13 @@ Value *CodeGenFunction::EmitAMDGPUBuiltinExpr(unsigned BuiltinID,
case AMDGPU::BI__builtin_amdgcn_atomic_inc64:
case AMDGPU::BI__builtin_amdgcn_atomic_dec32:
case AMDGPU::BI__builtin_amdgcn_atomic_dec64:
case AMDGPU::BI__builtin_amdgcn_ds_faddf:
case AMDGPU::BI__builtin_amdgcn_ds_atomic_fadd_f64:
case AMDGPU::BI__builtin_amdgcn_ds_atomic_fadd_f32:
case AMDGPU::BI__builtin_amdgcn_ds_atomic_fadd_v2f16:
case AMDGPU::BI__builtin_amdgcn_ds_atomic_fadd_v2bf16: {
case AMDGPU::BI__builtin_amdgcn_ds_atomic_fadd_v2bf16:
case AMDGPU::BI__builtin_amdgcn_ds_faddf:
case AMDGPU::BI__builtin_amdgcn_ds_fminf:
case AMDGPU::BI__builtin_amdgcn_ds_fmaxf: {
llvm::AtomicRMWInst::BinOp BinOp;
switch (BuiltinID) {
case AMDGPU::BI__builtin_amdgcn_atomic_inc32:
Expand All @@ -19109,6 +19089,12 @@ Value *CodeGenFunction::EmitAMDGPUBuiltinExpr(unsigned BuiltinID,
case AMDGPU::BI__builtin_amdgcn_ds_atomic_fadd_v2bf16:
BinOp = llvm::AtomicRMWInst::FAdd;
break;
case AMDGPU::BI__builtin_amdgcn_ds_fminf:
BinOp = llvm::AtomicRMWInst::FMin;
break;
case AMDGPU::BI__builtin_amdgcn_ds_fmaxf:
BinOp = llvm::AtomicRMWInst::FMax;
break;
}

Address Ptr = CheckAtomicAlignment(*this, E);
Expand All @@ -19118,8 +19104,10 @@ Value *CodeGenFunction::EmitAMDGPUBuiltinExpr(unsigned BuiltinID,

bool Volatile;

if (BuiltinID == AMDGPU::BI__builtin_amdgcn_ds_faddf) {
// __builtin_amdgcn_ds_faddf has an explicit volatile argument
if (BuiltinID == AMDGPU::BI__builtin_amdgcn_ds_faddf ||
BuiltinID == AMDGPU::BI__builtin_amdgcn_ds_fminf ||
BuiltinID == AMDGPU::BI__builtin_amdgcn_ds_fmaxf) {
// __builtin_amdgcn_ds_faddf/fminf/fmaxf has an explicit volatile argument
Volatile =
cast<ConstantInt>(EmitScalarExpr(E->getArg(4)))->getZExtValue();
} else {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ clang_tablegen(OpenCLBuiltins.inc -gen-clang-opencl-builtins

add_clang_library(clangSema
AnalysisBasedWarnings.cpp
CheckExprLifetime.cpp
CodeCompleteConsumer.cpp
DeclSpec.cpp
DelayedDiagnostic.cpp
Expand Down
1,259 changes: 1,259 additions & 0 deletions clang/lib/Sema/CheckExprLifetime.cpp

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions clang/lib/Sema/CheckExprLifetime.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//===- CheckExprLifetime.h ----------------------------------- -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//===----------------------------------------------------------------------===//
//
// This files implements a statement-local lifetime analysis.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_SEMA_CHECK_EXPR_LIFETIME_H
#define LLVM_CLANG_SEMA_CHECK_EXPR_LIFETIME_H

#include "clang/AST/Expr.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Sema.h"

namespace clang::sema {

/// Check that the lifetime of the given expr (and its subobjects) is
/// sufficient for initializing the entity, and perform lifetime extension
/// (when permitted) if not.
void checkExprLifetime(Sema &SemaRef, const InitializedEntity &Entity,
Expr *Init);

} // namespace clang::sema

#endif // LLVM_CLANG_SEMA_CHECK_EXPR_LIFETIME_H
1,232 changes: 2 additions & 1,230 deletions clang/lib/Sema/SemaInit.cpp

Large diffs are not rendered by default.

1,064 changes: 165 additions & 899 deletions clang/lib/Sema/SemaOpenMP.cpp

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4372,8 +4372,8 @@ HLSLCompareFloatingRank(QualType LHS, QualType RHS) {
if (const auto *VT = RHS->getAs<VectorType>())
RHS = VT->getElementType();

const auto L = LHS->getAs<BuiltinType>()->getKind();
const auto R = RHS->getAs<BuiltinType>()->getKind();
const auto L = LHS->castAs<BuiltinType>()->getKind();
const auto R = RHS->castAs<BuiltinType>()->getKind();
if (L == R)
return ImplicitConversionSequence::Indistinguishable;
return L < R ? ImplicitConversionSequence::Better
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -6551,7 +6551,7 @@ QualType TreeTransform<Derived>::TransformTypeOfExprType(TypeLocBuilder &TLB,
return QualType();

QualType Result = TL.getType();
TypeOfKind Kind = Result->getAs<TypeOfExprType>()->getKind();
TypeOfKind Kind = Result->castAs<TypeOfExprType>()->getKind();
if (getDerived().AlwaysRebuild() || E.get() != TL.getUnderlyingExpr()) {
Result =
getDerived().RebuildTypeOfExprType(E.get(), TL.getTypeofLoc(), Kind);
Expand All @@ -6576,7 +6576,7 @@ QualType TreeTransform<Derived>::TransformTypeOfType(TypeLocBuilder &TLB,
return QualType();

QualType Result = TL.getType();
TypeOfKind Kind = Result->getAs<TypeOfType>()->getKind();
TypeOfKind Kind = Result->castAs<TypeOfType>()->getKind();
if (getDerived().AlwaysRebuild() || New_Under_TI != Old_Under_TI) {
Result = getDerived().RebuildTypeOfType(New_Under_TI->getType(), Kind);
if (Result.isNull())
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGen/coverage-target-attr.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %clang_cc1 -emit-llvm -coverage-notes-file=test.gcno -coverage-data-file=test.gcda -triple aarch64-linux-android30 -target-cpu generic -target-feature +tagged-globals -fsanitize=hwaddress %s -o %t
// RUN: %clang_cc1 -emit-llvm -coverage-notes-file=/dev/null -coverage-data-file=/dev/null -triple aarch64-linux-android30 -target-cpu generic -target-feature +tagged-globals -fsanitize=hwaddress %s -o %t
// RUN: FileCheck %s < %t

// CHECK: define internal void @__llvm_gcov_writeout() unnamed_addr [[ATTR:#[0-9]+]]
Expand Down
8 changes: 4 additions & 4 deletions clang/test/CodeGenCUDA/builtins-amdgcn.cu
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ __global__
// CHECK-NEXT: [[X_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[X]] to ptr
// CHECK-NEXT: store float [[SRC:%.*]], ptr [[SRC_ADDR_ASCAST]], align 4
// CHECK-NEXT: [[TMP0:%.*]] = load float, ptr [[SRC_ADDR_ASCAST]], align 4
// CHECK-NEXT: [[TMP1:%.*]] = call contract float @llvm.amdgcn.ds.fmax.f32(ptr addrspace(3) @_ZZ12test_ds_fmaxfE6shared, float [[TMP0]], i32 0, i32 0, i1 false)
// CHECK-NEXT: [[TMP1:%.*]] = atomicrmw fmax ptr addrspace(3) @_ZZ12test_ds_fmaxfE6shared, float [[TMP0]] monotonic, align 4
// CHECK-NEXT: store volatile float [[TMP1]], ptr [[X_ASCAST]], align 4
// CHECK-NEXT: ret void
//
Expand Down Expand Up @@ -142,7 +142,7 @@ __global__ void test_ds_fadd(float src) {
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[SHARED_ADDR_ASCAST]], align 8
// CHECK-NEXT: [[TMP2:%.*]] = addrspacecast ptr [[TMP1]] to ptr addrspace(3)
// CHECK-NEXT: [[TMP3:%.*]] = load float, ptr [[SRC_ADDR_ASCAST]], align 4
// CHECK-NEXT: [[TMP4:%.*]] = call contract float @llvm.amdgcn.ds.fmin.f32(ptr addrspace(3) [[TMP2]], float [[TMP3]], i32 0, i32 0, i1 false)
// CHECK-NEXT: [[TMP4:%.*]] = atomicrmw fmin ptr addrspace(3) [[TMP2]], float [[TMP3]] monotonic, align 4
// CHECK-NEXT: store volatile float [[TMP4]], ptr [[X_ASCAST]], align 4
// CHECK-NEXT: ret void
//
Expand Down Expand Up @@ -245,10 +245,10 @@ __device__ void func(float *x);
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[SHARED_ADDR_ASCAST]], align 8
// CHECK-NEXT: [[TMP2:%.*]] = addrspacecast ptr [[TMP1]] to ptr addrspace(3)
// CHECK-NEXT: [[TMP3:%.*]] = load float, ptr [[SRC_ADDR_ASCAST]], align 4
// CHECK-NEXT: [[TMP4:%.*]] = call contract float @llvm.amdgcn.ds.fmin.f32(ptr addrspace(3) [[TMP2]], float [[TMP3]], i32 0, i32 0, i1 false)
// CHECK-NEXT: [[TMP4:%.*]] = atomicrmw fmin ptr addrspace(3) [[TMP2]], float [[TMP3]] monotonic, align 4
// CHECK-NEXT: store volatile float [[TMP4]], ptr [[X_ASCAST]], align 4
// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[SHARED_ADDR_ASCAST]], align 8
// CHECK-NEXT: call void @_Z4funcPf(ptr noundef [[TMP5]]) #[[ATTR8:[0-9]+]]
// CHECK-NEXT: call void @_Z4funcPf(ptr noundef [[TMP5]]) #[[ATTR7:[0-9]+]]
// CHECK-NEXT: ret void
//
__global__ void test_ds_fmin_func(float src, float *__restrict shared) {
Expand Down
8 changes: 4 additions & 4 deletions clang/test/CodeGenCUDA/builtins-spirv-amdgcn.cu
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ __global__
// CHECK-NEXT: [[X_ASCAST:%.*]] = addrspacecast ptr [[X]] to ptr addrspace(4)
// CHECK-NEXT: store float [[SRC:%.*]], ptr addrspace(4) [[SRC_ADDR_ASCAST]], align 4
// CHECK-NEXT: [[TMP0:%.*]] = load float, ptr addrspace(4) [[SRC_ADDR_ASCAST]], align 4
// CHECK-NEXT: [[TMP1:%.*]] = call contract addrspace(4) float @llvm.amdgcn.ds.fmax.f32(ptr addrspace(3) @_ZZ12test_ds_fmaxfE6shared, float [[TMP0]], i32 0, i32 0, i1 false)
// CHECK-NEXT: [[TMP1:%.*]] = atomicrmw fmax ptr addrspace(3) @_ZZ12test_ds_fmaxfE6shared, float [[TMP0]] monotonic, align 4
// CHECK-NEXT: store volatile float [[TMP1]], ptr addrspace(4) [[X_ASCAST]], align 4
// CHECK-NEXT: ret void
//
Expand Down Expand Up @@ -139,7 +139,7 @@ __global__ void test_ds_fadd(float src) {
// CHECK-NEXT: [[TMP1:%.*]] = load ptr addrspace(4), ptr addrspace(4) [[SHARED_ADDR_ASCAST]], align 8
// CHECK-NEXT: [[TMP2:%.*]] = addrspacecast ptr addrspace(4) [[TMP1]] to ptr addrspace(3)
// CHECK-NEXT: [[TMP3:%.*]] = load float, ptr addrspace(4) [[SRC_ADDR_ASCAST]], align 4
// CHECK-NEXT: [[TMP4:%.*]] = call contract addrspace(4) float @llvm.amdgcn.ds.fmin.f32(ptr addrspace(3) [[TMP2]], float [[TMP3]], i32 0, i32 0, i1 false)
// CHECK-NEXT: [[TMP4:%.*]] = atomicrmw fmin ptr addrspace(3) [[TMP2]], float [[TMP3]] monotonic, align 4
// CHECK-NEXT: store volatile float [[TMP4]], ptr addrspace(4) [[X_ASCAST]], align 4
// CHECK-NEXT: ret void
//
Expand Down Expand Up @@ -236,10 +236,10 @@ __device__ void func(float *x);
// CHECK-NEXT: [[TMP1:%.*]] = load ptr addrspace(4), ptr addrspace(4) [[SHARED_ADDR_ASCAST]], align 8
// CHECK-NEXT: [[TMP2:%.*]] = addrspacecast ptr addrspace(4) [[TMP1]] to ptr addrspace(3)
// CHECK-NEXT: [[TMP3:%.*]] = load float, ptr addrspace(4) [[SRC_ADDR_ASCAST]], align 4
// CHECK-NEXT: [[TMP4:%.*]] = call contract addrspace(4) float @llvm.amdgcn.ds.fmin.f32(ptr addrspace(3) [[TMP2]], float [[TMP3]], i32 0, i32 0, i1 false)
// CHECK-NEXT: [[TMP4:%.*]] = atomicrmw fmin ptr addrspace(3) [[TMP2]], float [[TMP3]] monotonic, align 4
// CHECK-NEXT: store volatile float [[TMP4]], ptr addrspace(4) [[X_ASCAST]], align 4
// CHECK-NEXT: [[TMP5:%.*]] = load ptr addrspace(4), ptr addrspace(4) [[SHARED_ADDR_ASCAST]], align 8
// CHECK-NEXT: call spir_func addrspace(4) void @_Z4funcPf(ptr addrspace(4) noundef [[TMP5]]) #[[ATTR7:[0-9]+]]
// CHECK-NEXT: call spir_func addrspace(4) void @_Z4funcPf(ptr addrspace(4) noundef [[TMP5]]) #[[ATTR6:[0-9]+]]
// CHECK-NEXT: ret void
//
__global__ void test_ds_fmin_func(float src, float *__restrict shared) {
Expand Down
66 changes: 64 additions & 2 deletions clang/test/CodeGenOpenCL/builtins-amdgcn-vi.cl
Original file line number Diff line number Diff line change
Expand Up @@ -158,23 +158,85 @@ void test_ds_faddf(local float *out, float src) {
}

// CHECK-LABEL: @test_ds_fmin
// CHECK: {{.*}}call{{.*}} float @llvm.amdgcn.ds.fmin.f32(ptr addrspace(3) %out, float %src, i32 0, i32 0, i1 false)
// CHECK: atomicrmw fmin ptr addrspace(3) %out, float %src monotonic, align 4{{$}}
// CHECK: atomicrmw volatile fmin ptr addrspace(3) %out, float %src monotonic, align 4{{$}}

// CHECK: atomicrmw fmin ptr addrspace(3) %out, float %src acquire, align 4{{$}}
// CHECK: atomicrmw fmin ptr addrspace(3) %out, float %src acquire, align 4{{$}}
// CHECK: atomicrmw fmin ptr addrspace(3) %out, float %src release, align 4{{$}}
// CHECK: atomicrmw fmin ptr addrspace(3) %out, float %src acq_rel, align 4{{$}}
// CHECK: atomicrmw fmin ptr addrspace(3) %out, float %src seq_cst, align 4{{$}}
// CHECK: atomicrmw fmin ptr addrspace(3) %out, float %src seq_cst, align 4{{$}}

// CHECK: atomicrmw fmin ptr addrspace(3) %out, float %src syncscope("agent") monotonic, align 4{{$}}
// CHECK: atomicrmw fmin ptr addrspace(3) %out, float %src syncscope("workgroup") monotonic, align 4{{$}}
// CHECK: atomicrmw fmin ptr addrspace(3) %out, float %src syncscope("wavefront") monotonic, align 4{{$}}
// CHECK: atomicrmw fmin ptr addrspace(3) %out, float %src syncscope("singlethread") monotonic, align 4{{$}}
// CHECK: atomicrmw fmin ptr addrspace(3) %out, float %src monotonic, align 4{{$}}

#if !defined(__SPIRV__)
void test_ds_fminf(local float *out, float src) {
#else
void test_ds_fminf(__attribute__((address_space(3))) float *out, float src) {
#endif
*out = __builtin_amdgcn_ds_fminf(out, src, 0, 0, false);
*out = __builtin_amdgcn_ds_fminf(out, src, 0, 0, true);

// Test all orders.
*out = __builtin_amdgcn_ds_fminf(out, src, __ATOMIC_CONSUME, __MEMORY_SCOPE_SYSTEM, false);
*out = __builtin_amdgcn_ds_fminf(out, src, __ATOMIC_ACQUIRE, __MEMORY_SCOPE_SYSTEM, false);
*out = __builtin_amdgcn_ds_fminf(out, src, __ATOMIC_RELEASE, __MEMORY_SCOPE_SYSTEM, false);
*out = __builtin_amdgcn_ds_fminf(out, src, __ATOMIC_ACQ_REL, __MEMORY_SCOPE_SYSTEM, false);
*out = __builtin_amdgcn_ds_fminf(out, src, __ATOMIC_SEQ_CST, __MEMORY_SCOPE_SYSTEM, false);
*out = __builtin_amdgcn_ds_fminf(out, src, __ATOMIC_SEQ_CST, __MEMORY_SCOPE_SYSTEM, false); // invalid

// Test all syncscopes.
*out = __builtin_amdgcn_ds_fminf(out, src, __ATOMIC_RELAXED, __MEMORY_SCOPE_DEVICE, false);
*out = __builtin_amdgcn_ds_fminf(out, src, __ATOMIC_RELAXED, __MEMORY_SCOPE_WRKGRP, false);
*out = __builtin_amdgcn_ds_fminf(out, src, __ATOMIC_RELAXED, __MEMORY_SCOPE_WVFRNT, false);
*out = __builtin_amdgcn_ds_fminf(out, src, __ATOMIC_RELAXED, __MEMORY_SCOPE_SINGLE, false);
*out = __builtin_amdgcn_ds_fminf(out, src, __ATOMIC_RELAXED, 5, false); // invalid
}

// CHECK-LABEL: @test_ds_fmax
// CHECK: {{.*}}call{{.*}} float @llvm.amdgcn.ds.fmax.f32(ptr addrspace(3) %out, float %src, i32 0, i32 0, i1 false)
// CHECK: atomicrmw fmax ptr addrspace(3) %out, float %src monotonic, align 4{{$}}
// CHECK: atomicrmw volatile fmax ptr addrspace(3) %out, float %src monotonic, align 4{{$}}

// CHECK: atomicrmw fmax ptr addrspace(3) %out, float %src acquire, align 4{{$}}
// CHECK: atomicrmw fmax ptr addrspace(3) %out, float %src acquire, align 4{{$}}
// CHECK: atomicrmw fmax ptr addrspace(3) %out, float %src release, align 4{{$}}
// CHECK: atomicrmw fmax ptr addrspace(3) %out, float %src acq_rel, align 4{{$}}
// CHECK: atomicrmw fmax ptr addrspace(3) %out, float %src seq_cst, align 4{{$}}
// CHECK: atomicrmw fmax ptr addrspace(3) %out, float %src seq_cst, align 4{{$}}

// CHECK: atomicrmw fmax ptr addrspace(3) %out, float %src syncscope("agent") monotonic, align 4{{$}}
// CHECK: atomicrmw fmax ptr addrspace(3) %out, float %src syncscope("workgroup") monotonic, align 4{{$}}
// CHECK: atomicrmw fmax ptr addrspace(3) %out, float %src syncscope("wavefront") monotonic, align 4{{$}}
// CHECK: atomicrmw fmax ptr addrspace(3) %out, float %src syncscope("singlethread") monotonic, align 4{{$}}
// CHECK: atomicrmw fmax ptr addrspace(3) %out, float %src monotonic, align 4{{$}}

#if !defined(__SPIRV__)
void test_ds_fmaxf(local float *out, float src) {
#else
void test_ds_fmaxf(__attribute__((address_space(3))) float *out, float src) {
#endif
*out = __builtin_amdgcn_ds_fmaxf(out, src, 0, 0, false);
*out = __builtin_amdgcn_ds_fmaxf(out, src, 0, 0, true);

// Test all orders.
*out = __builtin_amdgcn_ds_fmaxf(out, src, __ATOMIC_CONSUME, __MEMORY_SCOPE_SYSTEM, false);
*out = __builtin_amdgcn_ds_fmaxf(out, src, __ATOMIC_ACQUIRE, __MEMORY_SCOPE_SYSTEM, false);
*out = __builtin_amdgcn_ds_fmaxf(out, src, __ATOMIC_RELEASE, __MEMORY_SCOPE_SYSTEM, false);
*out = __builtin_amdgcn_ds_fmaxf(out, src, __ATOMIC_ACQ_REL, __MEMORY_SCOPE_SYSTEM, false);
*out = __builtin_amdgcn_ds_fmaxf(out, src, __ATOMIC_SEQ_CST, __MEMORY_SCOPE_SYSTEM, false);
*out = __builtin_amdgcn_ds_fmaxf(out, src, __ATOMIC_SEQ_CST, __MEMORY_SCOPE_SYSTEM, false); // invalid

// Test all syncscopes.
*out = __builtin_amdgcn_ds_fmaxf(out, src, __ATOMIC_RELAXED, __MEMORY_SCOPE_DEVICE, false);
*out = __builtin_amdgcn_ds_fmaxf(out, src, __ATOMIC_RELAXED, __MEMORY_SCOPE_WRKGRP, false);
*out = __builtin_amdgcn_ds_fmaxf(out, src, __ATOMIC_RELAXED, __MEMORY_SCOPE_WVFRNT, false);
*out = __builtin_amdgcn_ds_fmaxf(out, src, __ATOMIC_RELAXED, __MEMORY_SCOPE_SINGLE, false);
*out = __builtin_amdgcn_ds_fmaxf(out, src, __ATOMIC_RELAXED, 5, false); // invalid
}

// CHECK-LABEL: @test_s_memtime
Expand Down
17 changes: 17 additions & 0 deletions clang/test/PCH/cxx2a-requires-expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,20 @@ bool f() {
requires C<typename T::val> || (C<typename T::val> || C<T>);
};
}

namespace trailing_requires_expression {

template <typename T> requires C<T> && C2<T, T>
// CHECK: template <typename T> requires C<T> && C2<T, T> void g();
void g();

template <typename T> requires C<T> || C2<T, T>
// CHECK: template <typename T> requires C<T> || C2<T, T> constexpr int h = sizeof(T);
constexpr int h = sizeof(T);

template <typename T> requires C<T>
// CHECK: template <typename T> requires C<T> class i {
// CHECK-NEXT: };
class i {};

}
19 changes: 10 additions & 9 deletions flang/docs/Intrinsics.md
Original file line number Diff line number Diff line change
Expand Up @@ -893,16 +893,17 @@ used in constant expressions have currently no folding support at all.
##### `CMDSTAT`:

- Synchronous execution:
- -2: No error condition occurs, but `WAIT` is present with the value `false`, and the processor does not support asynchronous execution.
- -1: The processor does not support command line execution.
- -2: `ASYNC_NO_SUPPORT_ERR` - No error condition occurs, but `WAIT` is present with the value `false`, and the processor does not support asynchronous execution.
- -1: `NO_SUPPORT_ERR` - The processor does not support command line execution. (system returns -1 with errno `ENOENT`)
- 0: `CMD_EXECUTED` - Command executed with no error.
- \+ (positive value): An error condition occurs.
- 1: Fork Error (occurs only on POSIX-compatible systems).
- 2: Execution Error (command exits with status -1).
- 3: Invalid Command Error (determined by the exit code depending on the system).
- On Windows: exit code is 1.
- On POSIX-compatible systems: exit code is 127 or 126.
- 4: Signal error (either stopped or killed by signal, occurs only on POSIX-compatible systems).
- 0: Otherwise.
- 1: `FORK_ERR` - Fork Error (occurs only on POSIX-compatible systems).
- 2: `EXECL_ERR` - Execution Error (system returns -1 with other errno).
- 3: `COMMAND_EXECUTION_ERR` - Invalid Command Error (exit code 1).
- 4: `COMMAND_CANNOT_EXECUTE_ERR` - Command Cannot Execute Error (Linux exit code 126).
- 5: `COMMAND_NOT_FOUND_ERR` - Command Not Found Error (Linux exit code 127).
- 6: `INVALID_CL_ERR` - Invalid Command Line Error (covers all other non-zero exit codes).
- 7: `SIGNAL_ERR` - Signal error (either stopped or killed by signal, occurs only on POSIX-compatible systems).
- Asynchronous execution:
- 0 will always be assigned.

Expand Down
3 changes: 2 additions & 1 deletion flang/lib/Lower/OpenMP/ClauseProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1027,7 +1027,8 @@ bool ClauseProcessor::processReduction(

// Copy local lists into the output.
llvm::copy(reductionVars, std::back_inserter(result.reductionVars));
llvm::copy(reduceVarByRef, std::back_inserter(result.reduceVarByRef));
llvm::copy(reduceVarByRef,
std::back_inserter(result.reductionVarsByRef));
llvm::copy(reductionDeclSymbols,
std::back_inserter(result.reductionDeclSymbols));

Expand Down
121 changes: 96 additions & 25 deletions flang/runtime/execute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
#include "tools.h"
#include "flang/Runtime/descriptor.h"
#include <cstdlib>
#include <errno.h>
#include <future>
#include <limits>

#ifdef _WIN32
#include "flang/Common/windows-include.h"
#else
Expand All @@ -32,13 +34,16 @@ namespace Fortran::runtime {
// and the processor does not support asynchronous execution. Otherwise it is
// assigned the value 0
enum CMD_STAT {
ASYNC_NO_SUPPORT_ERR = -2,
NO_SUPPORT_ERR = -1,
CMD_EXECUTED = 0,
FORK_ERR = 1,
EXECL_ERR = 2,
INVALID_CL_ERR = 3,
SIGNAL_ERR = 4
ASYNC_NO_SUPPORT_ERR = -2, // system returns -1 with ENOENT
NO_SUPPORT_ERR = -1, // Linux setsid() returns -1
CMD_EXECUTED = 0, // command executed with no error
FORK_ERR = 1, // Linux fork() returns < 0
EXECL_ERR = 2, // system returns -1 with other errno
COMMAND_EXECUTION_ERR = 3, // exit code 1
COMMAND_CANNOT_EXECUTE_ERR = 4, // Linux exit code 126
COMMAND_NOT_FOUND_ERR = 5, // Linux exit code 127
INVALID_CL_ERR = 6, // cover all other non-zero exit code
SIGNAL_ERR = 7
};

// Override CopyCharsToDescriptor in tools.h, pass string directly
Expand All @@ -62,24 +67,86 @@ void CheckAndStoreIntToDescriptor(

// If a condition occurs that would assign a nonzero value to CMDSTAT but
// the CMDSTAT variable is not present, error termination is initiated.
int TerminationCheck(int status, const Descriptor *cmdstat,
std::int64_t TerminationCheck(std::int64_t status, const Descriptor *cmdstat,
const Descriptor *cmdmsg, Terminator &terminator) {
// On both Windows and Linux, errno is set when system returns -1.
if (status == -1) {
if (!cmdstat) {
terminator.Crash("Execution error with system status code: %d", status);
// On Windows, ENOENT means the command interpreter can't be found.
// On Linux, system calls execl with filepath "/bin/sh", ENOENT means the
// file pathname does not exist.
if (errno == ENOENT) {
if (!cmdstat) {
terminator.Crash("Command line execution is not supported, system "
"returns -1 with errno ENOENT.");
} else {
StoreIntToDescriptor(cmdstat, NO_SUPPORT_ERR, terminator);
CheckAndCopyCharsToDescriptor(cmdmsg,
"Command line execution is not supported, system returns -1 with "
"errno ENOENT.");
}
} else {
StoreIntToDescriptor(cmdstat, EXECL_ERR, terminator);
CheckAndCopyCharsToDescriptor(cmdmsg, "Execution error");
char err_buffer[30];
char msg[]{"Execution error with system status code: -1, errno: "};
#ifdef _WIN32
if (strerror_s(err_buffer, sizeof(err_buffer), errno) != 0)
#else
if (strerror_r(errno, err_buffer, sizeof(err_buffer)) != 0)
#endif
terminator.Crash("errno to char msg failed.");
char *newMsg{static_cast<char *>(AllocateMemoryOrCrash(
terminator, std::strlen(msg) + std::strlen(err_buffer) + 1))};
std::strcat(newMsg, err_buffer);

if (!cmdstat) {
terminator.Crash(newMsg);
} else {
StoreIntToDescriptor(cmdstat, EXECL_ERR, terminator);
CheckAndCopyCharsToDescriptor(cmdmsg, newMsg);
}
FreeMemory(newMsg);
}
}

#ifdef _WIN32
// On WIN32 API std::system returns exit status directly
int exitStatusVal{status};
if (exitStatusVal == 1) {
std::int64_t exitStatusVal{status};
if (exitStatusVal != 0) {
if (!cmdstat) {
terminator.Crash(
"Invalid command quit with exit status code: %d", exitStatusVal);
} else {
StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
}
}
#else
int exitStatusVal{WEXITSTATUS(status)};
if (exitStatusVal == 127 || exitStatusVal == 126) {
#endif
std::int64_t exitStatusVal{WEXITSTATUS(status)};
if (exitStatusVal == 1) {
if (!cmdstat) {
terminator.Crash("Command line execution failed with exit code: 1.");
} else {
StoreIntToDescriptor(cmdstat, COMMAND_EXECUTION_ERR, terminator);
CheckAndCopyCharsToDescriptor(
cmdmsg, "Command line execution failed with exit code: 1.");
}
} else if (exitStatusVal == 126) {
if (!cmdstat) {
terminator.Crash("Command cannot be executed with exit code: 126.");
} else {
StoreIntToDescriptor(cmdstat, COMMAND_CANNOT_EXECUTE_ERR, terminator);
CheckAndCopyCharsToDescriptor(
cmdmsg, "Command cannot be executed with exit code: 126.");
}
} else if (exitStatusVal == 127) {
if (!cmdstat) {
terminator.Crash("Command not found with exit code: 127.");
} else {
StoreIntToDescriptor(cmdstat, COMMAND_NOT_FOUND_ERR, terminator);
CheckAndCopyCharsToDescriptor(
cmdmsg, "Command not found with exit code: 127.");
}
// capture all other nonzero exit code
} else if (exitStatusVal != 0) {
if (!cmdstat) {
terminator.Crash(
"Invalid command quit with exit status code: %d", exitStatusVal);
Expand All @@ -88,23 +155,26 @@ int TerminationCheck(int status, const Descriptor *cmdstat,
CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
}
}
#endif

#if defined(WIFSIGNALED) && defined(WTERMSIG)
if (WIFSIGNALED(status)) {
if (!cmdstat) {
terminator.Crash("killed by signal: %d", WTERMSIG(status));
terminator.Crash("Killed by signal: %d", WTERMSIG(status));
} else {
StoreIntToDescriptor(cmdstat, SIGNAL_ERR, terminator);
CheckAndCopyCharsToDescriptor(cmdmsg, "killed by signal");
CheckAndCopyCharsToDescriptor(cmdmsg, "Killed by signal");
}
}
#endif

#if defined(WIFSTOPPED) && defined(WSTOPSIG)
if (WIFSTOPPED(status)) {
if (!cmdstat) {
terminator.Crash("stopped by signal: %d", WSTOPSIG(status));
terminator.Crash("Stopped by signal: %d", WSTOPSIG(status));
} else {
StoreIntToDescriptor(cmdstat, SIGNAL_ERR, terminator);
CheckAndCopyCharsToDescriptor(cmdmsg, "stopped by signal");
CheckAndCopyCharsToDescriptor(cmdmsg, "Stopped by signal");
}
}
#endif
Expand Down Expand Up @@ -134,8 +204,9 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,

if (wait) {
// either wait is not specified or wait is true: synchronous mode
int status{std::system(newCmd)};
int exitStatusVal{TerminationCheck(status, cmdstat, cmdmsg, terminator)};
std::int64_t status{std::system(newCmd)};
std::int64_t exitStatusVal{
TerminationCheck(status, cmdstat, cmdmsg, terminator)};
// If sync, assigned processor-dependent exit status. Otherwise unchanged
CheckAndStoreIntToDescriptor(exitstat, exitStatusVal, terminator);
} else {
Expand Down Expand Up @@ -173,7 +244,7 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
terminator.Crash(
"CreateProcess failed with error code: %lu.", GetLastError());
} else {
StoreIntToDescriptor(cmdstat, (uint32_t)GetLastError(), terminator);
StoreIntToDescriptor(cmdstat, ASYNC_NO_SUPPORT_ERR, terminator);
CheckAndCopyCharsToDescriptor(cmdmsg, "CreateProcess failed.");
}
}
Expand Down Expand Up @@ -201,7 +272,7 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
}
exit(EXIT_FAILURE);
}
int status{std::system(newCmd)};
std::int64_t status{std::system(newCmd)};
TerminationCheck(status, cmdstat, cmdmsg, terminator);
exit(status);
}
Expand Down
82 changes: 65 additions & 17 deletions flang/unittests/Runtime/CommandTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,8 @@ TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); }
TEST_F(ZeroArguments, ECLValidCommandAndPadSync) {
OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
bool wait{true};
OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};
OwningPtr<Descriptor> cmdStat{EmptyIntDescriptor()};
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};

RTNAME(ExecuteCommandLine)
Expand All @@ -339,40 +339,91 @@ TEST_F(ZeroArguments, ECLValidCommandStatusSetSync) {
CheckDescriptorEqStr(cmdMsg.get(), "No change");
}

TEST_F(ZeroArguments, ECLInvalidCommandErrorSync) {
OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
TEST_F(ZeroArguments, ECLGeneralErrorCommandErrorSync) {
OwningPtr<Descriptor> command{CharDescriptor("cat GeneralErrorCommand")};
bool wait{true};
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXXXXXXXX")};

RTNAME(ExecuteCommandLine)
(*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
#ifdef _WIN32
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXXXX");
#else
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
CheckDescriptorEqStr(cmdMsg.get(), "Command line execution failed");
#endif
}

TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
OwningPtr<Descriptor> command{CharDescriptor(
"touch NotExecutedCommandFile && chmod -x NotExecutedCommandFile && "
"./NotExecutedCommandFile")};
bool wait{true};
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXX")};

RTNAME(ExecuteCommandLine)
(*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
#ifdef _WIN32
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXX");
#else
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 126);
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 4);
CheckDescriptorEqStr(cmdMsg.get(), "Command cannot be execu");
// removing the file only on Linux (file is not created on Win)
OwningPtr<Descriptor> commandClean{
CharDescriptor("rm -f NotExecutedCommandFile")};
OwningPtr<Descriptor> cmdMsgNoErr{CharDescriptor("No Error")};
RTNAME(ExecuteCommandLine)
(*commandClean.get(), wait, exitStat.get(), cmdStat.get(), cmdMsgNoErr.get());
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);
CheckDescriptorEqStr(cmdMsgNoErr.get(), "No Error");
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
#endif
}

TEST_F(ZeroArguments, ECLNotFoundCommandErrorSync) {
OwningPtr<Descriptor> command{CharDescriptor("NotFoundCommand")};
bool wait{true};
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
OwningPtr<Descriptor> cmdMsg{CharDescriptor("Message ChangedXXXXXXXXX")};
OwningPtr<Descriptor> cmdMsg{CharDescriptor("unmodified buffer XXXXXXXXX")};

RTNAME(ExecuteCommandLine)
(*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
#ifdef _WIN32
CheckDescriptorEqInt(exitStat.get(), 1);
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXX");
#else
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 5);
CheckDescriptorEqStr(cmdMsg.get(), "Command not found with exit");
#endif
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXX");
}

TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {
OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
bool wait{true};
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
OwningPtr<Descriptor> cmdMsg{CharDescriptor("No Change")};

#ifdef _WIN32
EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
*command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()),
*command.get(), wait, nullptr, nullptr, cmdMsg.get()),
"Invalid command quit with exit status code: 1");
#else
EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
*command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()),
"Invalid command quit with exit status code: 127");
*command.get(), wait, nullptr, nullptr, cmdMsg.get()),
"Command not found with exit code: 127.");
#endif
CheckDescriptorEqInt(exitStat.get(), 404);
CheckDescriptorEqStr(cmdMsg.get(), "No Change");
}

Expand All @@ -394,13 +445,10 @@ TEST_F(ZeroArguments, ECLValidCommandAndExitStatNoChangeAndCMDStatusSetAsync) {
TEST_F(ZeroArguments, ECLInvalidCommandParentNotTerminatedAsync) {
OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
bool wait{false};
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};

EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
*command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()));

CheckDescriptorEqInt(exitStat.get(), 404);
*command.get(), wait, nullptr, nullptr, cmdMsg.get()));
CheckDescriptorEqStr(cmdMsg.get(), "No change");
}

Expand Down
17 changes: 10 additions & 7 deletions lldb/bindings/python/python-swigsafecast.swig
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ PythonObject SWIGBridge::ToSWIGWrapper(const Status& status) {
return ToSWIGHelper(new lldb::SBError(status), SWIGTYPE_p_lldb__SBError);
}

PythonObject SWIGBridge::ToSWIGWrapper(std::unique_ptr<lldb::SBStream> stream_sb) {
return ToSWIGHelper(stream_sb.release(), SWIGTYPE_p_lldb__SBStream);
}

PythonObject SWIGBridge::ToSWIGWrapper(std::unique_ptr<lldb::SBStructuredData> data_sb) {
return ToSWIGHelper(data_sb.release(), SWIGTYPE_p_lldb__SBStructuredData);
}
Expand Down Expand Up @@ -115,9 +111,16 @@ SWIGBridge::ToSWIGWrapper(CommandReturnObject &cmd_retobj) {
SWIGTYPE_p_lldb__SBCommandReturnObject);
}

ScopedPythonObject<lldb::SBEvent> SWIGBridge::ToSWIGWrapper(Event *event) {
return ScopedPythonObject<lldb::SBEvent>(new lldb::SBEvent(event),
SWIGTYPE_p_lldb__SBEvent);
PythonObject SWIGBridge::ToSWIGWrapper(const Stream *s) {
return ToSWIGHelper(new lldb::SBStream(), SWIGTYPE_p_lldb__SBStream);
}

PythonObject SWIGBridge::ToSWIGWrapper(std::shared_ptr<lldb::SBStream> stream_sb) {
return ToSWIGHelper(stream_sb.get(), SWIGTYPE_p_lldb__SBStream);
}

PythonObject SWIGBridge::ToSWIGWrapper(Event *event) {
return ToSWIGHelper(new lldb::SBEvent(event), SWIGTYPE_p_lldb__SBEvent);
}

PythonObject SWIGBridge::ToSWIGWrapper(
Expand Down
157 changes: 27 additions & 130 deletions lldb/bindings/python/python-wrapper.swig
Original file line number Diff line number Diff line change
Expand Up @@ -229,133 +229,6 @@ PythonObject lldb_private::python::SWIGBridge::LLDBSwigPythonCreateCommandObject
return pfunc(SWIGBridge::ToSWIGWrapper(std::move(debugger_sp)), dict);
}

PythonObject lldb_private::python::SWIGBridge::LLDBSwigPythonCreateScriptedThreadPlan(
const char *python_class_name, const char *session_dictionary_name,
const lldb_private::StructuredDataImpl &args_impl,
std::string &error_string, const lldb::ThreadPlanSP &thread_plan_sp) {
if (python_class_name == NULL || python_class_name[0] == '\0' ||
!session_dictionary_name)
return PythonObject();

PyErr_Cleaner py_err_cleaner(true);

auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(
session_dictionary_name);
auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(
python_class_name, dict);

if (!pfunc.IsAllocated()) {
error_string.append("could not find script class: ");
error_string.append(python_class_name);
return PythonObject();
}

PythonObject tp_arg = SWIGBridge::ToSWIGWrapper(thread_plan_sp);

llvm::Expected<PythonCallable::ArgInfo> arg_info = pfunc.GetArgInfo();
if (!arg_info) {
llvm::handleAllErrors(
arg_info.takeError(),
[&](PythonException &E) { error_string.append(E.ReadBacktrace()); },
[&](const llvm::ErrorInfoBase &E) {
error_string.append(E.message());
});
return PythonObject();
}

PythonObject result = {};
auto args_sb = std::unique_ptr<lldb::SBStructuredData>(new lldb::SBStructuredData(args_impl));
if (arg_info.get().max_positional_args == 2) {
if (args_sb->IsValid()) {
error_string.assign(
"args passed, but __init__ does not take an args dictionary");
return PythonObject();
}
result = pfunc(tp_arg, dict);
} else if (arg_info.get().max_positional_args >= 3) {
result = pfunc(tp_arg, SWIGBridge::ToSWIGWrapper(std::move(args_sb)), dict);
} else {
error_string.assign("wrong number of arguments in __init__, should be 2 or "
"3 (not including self)");
return PythonObject();
}

// FIXME: At this point we should check that the class we found supports all
// the methods that we need.

return result;
}

bool lldb_private::python::SWIGBridge::LLDBSWIGPythonCallThreadPlan(
void *implementer, const char *method_name, lldb_private::Event *event,
bool &got_error) {
got_error = false;

PyErr_Cleaner py_err_cleaner(false);
PythonObject self(PyRefType::Borrowed, static_cast<PyObject *>(implementer));
auto pfunc = self.ResolveName<PythonCallable>(method_name);

if (!pfunc.IsAllocated())
return false;

PythonObject result;
if (event != nullptr) {
ScopedPythonObject<SBEvent> event_arg = SWIGBridge::ToSWIGWrapper(event);
result = pfunc(event_arg.obj());
} else
result = pfunc();

if (PyErr_Occurred()) {
got_error = true;
printf("Return value was neither false nor true for call to %s.\n",
method_name);
PyErr_Print();
return false;
}

if (result.get() == Py_True)
return true;
else if (result.get() == Py_False)
return false;

// Somebody returned the wrong thing...
got_error = true;
printf("Wrong return value type for call to %s.\n", method_name);
return false;
}

bool lldb_private::python::SWIGBridge::LLDBSWIGPythonCallThreadPlan(
void *implementer, const char *method_name, lldb_private::Stream *stream,
bool &got_error) {
got_error = false;

PyErr_Cleaner py_err_cleaner(false);
PythonObject self(PyRefType::Borrowed, static_cast<PyObject *>(implementer));
auto pfunc = self.ResolveName<PythonCallable>(method_name);

if (!pfunc.IsAllocated())
return false;

auto *sb_stream = new lldb::SBStream();
PythonObject sb_stream_arg =
SWIGBridge::ToSWIGWrapper(std::unique_ptr<lldb::SBStream>(sb_stream));

PythonObject result;
result = pfunc(sb_stream_arg);

if (PyErr_Occurred()) {
printf("Error occured for call to %s.\n",
method_name);
PyErr_Print();
got_error = true;
return false;
}
if (stream)
stream->PutCString(sb_stream->GetData());
return true;

}

PythonObject lldb_private::python::SWIGBridge::LLDBSwigPythonCreateScriptedBreakpointResolver(
const char *python_class_name, const char *session_dictionary_name,
const StructuredDataImpl &args_impl,
Expand Down Expand Up @@ -500,9 +373,8 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonStopHookCallHandleStop(
if (!pfunc.IsAllocated())
return true;

auto *sb_stream = new lldb::SBStream();
PythonObject sb_stream_arg =
SWIGBridge::ToSWIGWrapper(std::unique_ptr<lldb::SBStream>(sb_stream));
std::shared_ptr<lldb::SBStream> sb_stream = std::make_shared<lldb::SBStream>();
PythonObject sb_stream_arg = SWIGBridge::ToSWIGWrapper(sb_stream);
PythonObject result =
pfunc(SWIGBridge::ToSWIGWrapper(std::move(exc_ctx_sp)), sb_stream_arg);

Expand All @@ -517,6 +389,7 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonStopHookCallHandleStop(
// makes an internally help StreamString which I can't interpose, so I
// have to copy it over here.
stream->PutCString(sb_stream->GetData());
sb_stream_arg.release();

if (result.get() == Py_False)
return false;
Expand Down Expand Up @@ -753,6 +626,30 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBError(PyObject * data
return sb_ptr;
}

void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBEvent(PyObject * data) {
lldb::SBEvent *sb_ptr = nullptr;

int valid_cast =
SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBEvent, 0);

if (valid_cast == -1)
return NULL;

return sb_ptr;
}

void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBStream(PyObject * data) {
lldb::SBStream *sb_ptr = nullptr;

int valid_cast =
SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBStream, 0);

if (valid_cast == -1)
return NULL;

return sb_ptr;
}

void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBValue(PyObject * data) {
lldb::SBValue *sb_ptr = NULL;

Expand Down
4 changes: 3 additions & 1 deletion lldb/include/lldb/API/SBEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <vector>

namespace lldb_private {
class ScriptInterpreter;
namespace python {
class SWIGBridge;
}
Expand Down Expand Up @@ -73,11 +74,12 @@ class LLDB_API SBEvent {
friend class SBThread;
friend class SBWatchpoint;

friend class lldb_private::ScriptInterpreter;
friend class lldb_private::python::SWIGBridge;

SBEvent(lldb::EventSP &event_sp);

SBEvent(lldb_private::Event *event_sp);
SBEvent(lldb_private::Event *event);

lldb::EventSP &GetSP() const;

Expand Down
6 changes: 6 additions & 0 deletions lldb/include/lldb/API/SBStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@

#include "lldb/API/SBDefines.h"

namespace lldb_private {
class ScriptInterpreter;
} // namespace lldb_private

namespace lldb {

class LLDB_API SBStream {
Expand Down Expand Up @@ -103,6 +107,8 @@ class LLDB_API SBStream {
friend class SBValue;
friend class SBWatchpoint;

friend class lldb_private::ScriptInterpreter;

lldb_private::Stream *operator->();

lldb_private::Stream *get();
Expand Down
4 changes: 2 additions & 2 deletions lldb/include/lldb/Interpreter/Interfaces/ScriptedInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDINTERFACE_H

#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/UnimplementedError.h"
Expand Down Expand Up @@ -52,7 +51,8 @@ class ScriptedInterface {
}

template <typename T = StructuredData::ObjectSP>
bool CheckStructuredDataObject(llvm::StringRef caller, T obj, Status &error) {
static bool CheckStructuredDataObject(llvm::StringRef caller, T obj,
Status &error) {
if (!obj)
return ErrorWithMessage<bool>(caller, "Null Structured Data object",
error);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===-- ScriptedThreadPlanInterface.h ---------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_INTERPRETER_INTERFACES_SCRIPTEDTHREADPLANINTERFACE_H
#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDTHREADPLANINTERFACE_H

#include "lldb/lldb-private.h"

#include "ScriptedInterface.h"

namespace lldb_private {
class ScriptedThreadPlanInterface : public ScriptedInterface {
public:
virtual llvm::Expected<StructuredData::GenericSP>
CreatePluginObject(llvm::StringRef class_name,
lldb::ThreadPlanSP thread_plan_sp,
const StructuredDataImpl &args_sp) = 0;

virtual llvm::Expected<bool> ExplainsStop(Event *event) { return true; }

virtual llvm::Expected<bool> ShouldStop(Event *event) { return true; }

virtual llvm::Expected<bool> IsStale() { return true; };

virtual lldb::StateType GetRunState() { return lldb::eStateStepping; }

virtual llvm::Expected<bool> GetStopDescription(lldb_private::Stream *s) {
return true;
}
};
} // namespace lldb_private

#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDTHREADPLANINTERFACE_H
55 changes: 11 additions & 44 deletions lldb/include/lldb/Interpreter/ScriptInterpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
#include "lldb/API/SBBreakpoint.h"
#include "lldb/API/SBData.h"
#include "lldb/API/SBError.h"
#include "lldb/API/SBEvent.h"
#include "lldb/API/SBLaunchInfo.h"
#include "lldb/API/SBMemoryRegionInfo.h"
#include "lldb/API/SBStream.h"
#include "lldb/Breakpoint/BreakpointOptions.h"
#include "lldb/Core/PluginInterface.h"
#include "lldb/Core/SearchFilter.h"
Expand Down Expand Up @@ -250,50 +252,6 @@ class ScriptInterpreter : public PluginInterface {
return lldb::ValueObjectListSP();
}

virtual StructuredData::ObjectSP
CreateScriptedThreadPlan(const char *class_name,
const StructuredDataImpl &args_data,
std::string &error_str,
lldb::ThreadPlanSP thread_plan_sp) {
return StructuredData::ObjectSP();
}

virtual bool
ScriptedThreadPlanExplainsStop(StructuredData::ObjectSP implementor_sp,
Event *event, bool &script_error) {
script_error = true;
return true;
}

virtual bool
ScriptedThreadPlanShouldStop(StructuredData::ObjectSP implementor_sp,
Event *event, bool &script_error) {
script_error = true;
return true;
}

virtual bool
ScriptedThreadPlanIsStale(StructuredData::ObjectSP implementor_sp,
bool &script_error) {
script_error = true;
return true;
}

virtual lldb::StateType
ScriptedThreadPlanGetRunState(StructuredData::ObjectSP implementor_sp,
bool &script_error) {
script_error = true;
return lldb::eStateStepping;
}

virtual bool
ScriptedThreadPlanGetStopDescription(StructuredData::ObjectSP implementor_sp,
lldb_private::Stream *stream,
bool &script_error) {
script_error = true;
return false;
}

virtual StructuredData::GenericSP
CreateScriptedBreakpointResolver(const char *class_name,
const StructuredDataImpl &args_data,
Expand Down Expand Up @@ -592,6 +550,11 @@ class ScriptInterpreter : public PluginInterface {
return {};
}

virtual lldb::ScriptedThreadPlanInterfaceSP
CreateScriptedThreadPlanInterface() {
return {};
}

virtual lldb::OperatingSystemInterfaceSP CreateOperatingSystemInterface() {
return {};
}
Expand All @@ -610,6 +573,10 @@ class ScriptInterpreter : public PluginInterface {

Status GetStatusFromSBError(const lldb::SBError &error) const;

Event *GetOpaqueTypeFromSBEvent(const lldb::SBEvent &event) const;

Stream *GetOpaqueTypeFromSBStream(const lldb::SBStream &stream) const;

lldb::BreakpointSP
GetOpaqueTypeFromSBBreakpoint(const lldb::SBBreakpoint &breakpoint) const;

Expand Down
7 changes: 7 additions & 0 deletions lldb/include/lldb/Target/RegisterFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,15 @@ class FieldEnum {
: m_value(value), m_name(std::move(name)) {}

void ToXML(Stream &strm) const;

void DumpToLog(Log *log) const;
};

typedef std::vector<Enumerator> Enumerators;

// GDB also includes a "size" that is the size of the underlying register.
// We will not store that here but instead use the size of the register
// this gets attached to when emitting XML.
FieldEnum(std::string id, const Enumerators &enumerators);

const Enumerators &GetEnumerators() const { return m_enumerators; }
Expand All @@ -44,6 +49,8 @@ class FieldEnum {

void ToXML(Stream &strm, unsigned size) const;

void DumpToLog(Log *log) const;

private:
std::string m_id;
Enumerators m_enumerators;
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/Target/ThreadPlanPython.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <string>

#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/Interpreter/Interfaces/ScriptedThreadPlanInterface.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/Target.h"
Expand Down Expand Up @@ -70,6 +71,7 @@ class ThreadPlanPython : public ThreadPlan {
StreamString m_stop_description; // Cache the stop description here
bool m_did_push;
bool m_stop_others;
lldb::ScriptedThreadPlanInterfaceSP m_interface;

ThreadPlanPython(const ThreadPlanPython &) = delete;
const ThreadPlanPython &operator=(const ThreadPlanPython &) = delete;
Expand Down
3 changes: 3 additions & 0 deletions lldb/include/lldb/lldb-forward.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ class ScriptedMetadata;
class ScriptedPlatformInterface;
class ScriptedProcessInterface;
class ScriptedThreadInterface;
class ScriptedThreadPlanInterface;
class ScriptedSyntheticChildren;
class SearchFilter;
class Section;
Expand Down Expand Up @@ -406,6 +407,8 @@ typedef std::unique_ptr<lldb_private::ScriptedProcessInterface>
ScriptedProcessInterfaceUP;
typedef std::shared_ptr<lldb_private::ScriptedThreadInterface>
ScriptedThreadInterfaceSP;
typedef std::shared_ptr<lldb_private::ScriptedThreadPlanInterface>
ScriptedThreadPlanInterfaceSP;
typedef std::shared_ptr<lldb_private::Section> SectionSP;
typedef std::unique_ptr<lldb_private::SectionList> SectionListUP;
typedef std::weak_ptr<lldb_private::Section> SectionWP;
Expand Down
7 changes: 6 additions & 1 deletion lldb/source/Core/DumpRegisterInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ void lldb_private::DoDumpRegisterInfo(
};
DumpList(strm, " In sets: ", in_sets, emit_set);

if (flags_type)
if (flags_type) {
strm.Printf("\n\n%s", flags_type->AsTable(terminal_width).c_str());

std::string enumerators = flags_type->DumpEnums(terminal_width);
if (enumerators.size())
strm << "\n\n" << enumerators;
}
}
13 changes: 13 additions & 0 deletions lldb/source/Interpreter/ScriptInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,19 @@ ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const {
return Status();
}

Event *
ScriptInterpreter::GetOpaqueTypeFromSBEvent(const lldb::SBEvent &event) const {
return event.m_opaque_ptr;
}

Stream *ScriptInterpreter::GetOpaqueTypeFromSBStream(
const lldb::SBStream &stream) const {
if (stream.m_opaque_up)
return const_cast<lldb::SBStream &>(stream).m_opaque_up.get();

return nullptr;
}

std::optional<MemoryRegionInfo>
ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo(
const lldb::SBMemoryRegionInfo &mem_region) const {
Expand Down
198 changes: 180 additions & 18 deletions lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4179,21 +4179,134 @@ struct GdbServerTargetInfo {
RegisterSetMap reg_set_map;
};

static std::vector<RegisterFlags::Field> ParseFlagsFields(XMLNode flags_node,
unsigned size) {
static FieldEnum::Enumerators ParseEnumEvalues(const XMLNode &enum_node) {
Log *log(GetLog(GDBRLog::Process));
// We will use the last instance of each value. Also we preserve the order
// of declaration in the XML, as it may not be numerical.
// For example, hardware may intially release with two states that softwware
// can read from a register field:
// 0 = startup, 1 = running
// If in a future hardware release, the designers added a pre-startup state:
// 0 = startup, 1 = running, 2 = pre-startup
// Now it makes more sense to list them in this logical order as opposed to
// numerical order:
// 2 = pre-startup, 1 = startup, 0 = startup
// This only matters for "register info" but let's trust what the server
// chose regardless.
std::map<uint64_t, FieldEnum::Enumerator> enumerators;

enum_node.ForEachChildElementWithName(
"evalue", [&enumerators, &log](const XMLNode &enumerator_node) {
std::optional<llvm::StringRef> name;
std::optional<uint64_t> value;

enumerator_node.ForEachAttribute(
[&name, &value, &log](const llvm::StringRef &attr_name,
const llvm::StringRef &attr_value) {
if (attr_name == "name") {
if (attr_value.size())
name = attr_value;
else
LLDB_LOG(log, "ProcessGDBRemote::ParseEnumEvalues "
"Ignoring empty name in evalue");
} else if (attr_name == "value") {
uint64_t parsed_value = 0;
if (llvm::to_integer(attr_value, parsed_value))
value = parsed_value;
else
LLDB_LOG(log,
"ProcessGDBRemote::ParseEnumEvalues "
"Invalid value \"{0}\" in "
"evalue",
attr_value.data());
} else
LLDB_LOG(log,
"ProcessGDBRemote::ParseEnumEvalues Ignoring "
"unknown attribute "
"\"{0}\" in evalue",
attr_name.data());

// Keep walking attributes.
return true;
});

if (value && name)
enumerators.insert_or_assign(
*value, FieldEnum::Enumerator(*value, name->str()));

// Find all evalue elements.
return true;
});

FieldEnum::Enumerators final_enumerators;
for (auto [_, enumerator] : enumerators)
final_enumerators.push_back(enumerator);

return final_enumerators;
}

static void
ParseEnums(XMLNode feature_node,
llvm::StringMap<std::unique_ptr<FieldEnum>> &registers_enum_types) {
Log *log(GetLog(GDBRLog::Process));

// The top level element is "<enum...".
feature_node.ForEachChildElementWithName(
"enum", [log, &registers_enum_types](const XMLNode &enum_node) {
std::string id;

enum_node.ForEachAttribute([&id](const llvm::StringRef &attr_name,
const llvm::StringRef &attr_value) {
if (attr_name == "id")
id = attr_value;

// There is also a "size" attribute that is supposed to be the size in
// bytes of the register this applies to. However:
// * LLDB doesn't need this information.
// * It is difficult to verify because you have to wait until the
// enum is applied to a field.
//
// So we will emit this attribute in XML for GDB's sake, but will not
// bother ingesting it.

// Walk all attributes.
return true;
});

if (!id.empty()) {
FieldEnum::Enumerators enumerators = ParseEnumEvalues(enum_node);
if (!enumerators.empty()) {
LLDB_LOG(log,
"ProcessGDBRemote::ParseEnums Found enum type \"{0}\"",
id);
registers_enum_types.insert_or_assign(
id, std::make_unique<FieldEnum>(id, enumerators));
}
}

// Find all <enum> elements.
return true;
});
}

static std::vector<RegisterFlags::Field> ParseFlagsFields(
XMLNode flags_node, unsigned size,
const llvm::StringMap<std::unique_ptr<FieldEnum>> &registers_enum_types) {
Log *log(GetLog(GDBRLog::Process));
const unsigned max_start_bit = size * 8 - 1;

// Process the fields of this set of flags.
std::vector<RegisterFlags::Field> fields;
flags_node.ForEachChildElementWithName("field", [&fields, max_start_bit,
&log](const XMLNode
&field_node) {
flags_node.ForEachChildElementWithName("field", [&fields, max_start_bit, &log,
&registers_enum_types](
const XMLNode
&field_node) {
std::optional<llvm::StringRef> name;
std::optional<unsigned> start;
std::optional<unsigned> end;
std::optional<llvm::StringRef> type;

field_node.ForEachAttribute([&name, &start, &end, max_start_bit,
field_node.ForEachAttribute([&name, &start, &end, &type, max_start_bit,
&log](const llvm::StringRef &attr_name,
const llvm::StringRef &attr_value) {
// Note that XML in general requires that each of these attributes only
Expand Down Expand Up @@ -4240,8 +4353,7 @@ static std::vector<RegisterFlags::Field> ParseFlagsFields(XMLNode flags_node,
attr_value.data());
}
} else if (attr_name == "type") {
// Type is a known attribute but we do not currently use it and it is
// not required.
type = attr_value;
} else {
LLDB_LOG(
log,
Expand All @@ -4254,14 +4366,55 @@ static std::vector<RegisterFlags::Field> ParseFlagsFields(XMLNode flags_node,
});

if (name && start && end) {
if (*start > *end) {
if (*start > *end)
LLDB_LOG(
log,
"ProcessGDBRemote::ParseFlagsFields Start {0} > end {1} in field "
"\"{2}\", ignoring",
*start, *end, name->data());
} else {
fields.push_back(RegisterFlags::Field(name->str(), *start, *end));
else {
if (RegisterFlags::Field::GetSizeInBits(*start, *end) > 64)
LLDB_LOG(log,
"ProcessGDBRemote::ParseFlagsFields Ignoring field \"{2}\" "
"that has "
"size > 64 bits, this is not supported",
name->data());
else {
// A field's type may be set to the name of an enum type.
const FieldEnum *enum_type = nullptr;
if (type && !type->empty()) {
auto found = registers_enum_types.find(*type);
if (found != registers_enum_types.end()) {
enum_type = found->second.get();

// No enumerator can exceed the range of the field itself.
uint64_t max_value =
RegisterFlags::Field::GetMaxValue(*start, *end);
for (const auto &enumerator : enum_type->GetEnumerators()) {
if (enumerator.m_value > max_value) {
enum_type = nullptr;
LLDB_LOG(
log,
"ProcessGDBRemote::ParseFlagsFields In enum \"{0}\" "
"evalue \"{1}\" with value {2} exceeds the maximum value "
"of field \"{3}\" ({4}), ignoring enum",
type->data(), enumerator.m_name, enumerator.m_value,
name->data(), max_value);
break;
}
}
} else {
LLDB_LOG(log,
"ProcessGDBRemote::ParseFlagsFields Could not find type "
"\"{0}\" "
"for field \"{1}\", ignoring",
type->data(), name->data());
}
}

fields.push_back(
RegisterFlags::Field(name->str(), *start, *end, enum_type));
}
}
}

Expand All @@ -4272,12 +4425,14 @@ static std::vector<RegisterFlags::Field> ParseFlagsFields(XMLNode flags_node,

void ParseFlags(
XMLNode feature_node,
llvm::StringMap<std::unique_ptr<RegisterFlags>> &registers_flags_types) {
llvm::StringMap<std::unique_ptr<RegisterFlags>> &registers_flags_types,
const llvm::StringMap<std::unique_ptr<FieldEnum>> &registers_enum_types) {
Log *log(GetLog(GDBRLog::Process));

feature_node.ForEachChildElementWithName(
"flags",
[&log, &registers_flags_types](const XMLNode &flags_node) -> bool {
[&log, &registers_flags_types,
&registers_enum_types](const XMLNode &flags_node) -> bool {
LLDB_LOG(log, "ProcessGDBRemote::ParseFlags Found flags node \"{0}\"",
flags_node.GetAttributeValue("id").c_str());

Expand Down Expand Up @@ -4310,7 +4465,7 @@ void ParseFlags(
if (id && size) {
// Process the fields of this set of flags.
std::vector<RegisterFlags::Field> fields =
ParseFlagsFields(flags_node, *size);
ParseFlagsFields(flags_node, *size, registers_enum_types);
if (fields.size()) {
// Sort so that the fields with the MSBs are first.
std::sort(fields.rbegin(), fields.rend());
Expand Down Expand Up @@ -4375,13 +4530,19 @@ void ParseFlags(
bool ParseRegisters(
XMLNode feature_node, GdbServerTargetInfo &target_info,
std::vector<DynamicRegisterInfo::Register> &registers,
llvm::StringMap<std::unique_ptr<RegisterFlags>> &registers_flags_types) {
llvm::StringMap<std::unique_ptr<RegisterFlags>> &registers_flags_types,
llvm::StringMap<std::unique_ptr<FieldEnum>> &registers_enum_types) {
if (!feature_node)
return false;

Log *log(GetLog(GDBRLog::Process));

ParseFlags(feature_node, registers_flags_types);
// Enums first because they are referenced by fields in the flags.
ParseEnums(feature_node, registers_enum_types);
for (const auto &enum_type : registers_enum_types)
enum_type.second->DumpToLog(log);

ParseFlags(feature_node, registers_flags_types, registers_enum_types);
for (const auto &flags : registers_flags_types)
flags.second->DumpToLog(log);

Expand Down Expand Up @@ -4643,7 +4804,7 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess(
if (arch_to_use.IsValid()) {
for (auto &feature_node : feature_nodes) {
ParseRegisters(feature_node, target_info, registers,
m_registers_flags_types);
m_registers_flags_types, m_registers_enum_types);
}

for (const auto &include : target_info.includes) {
Expand Down Expand Up @@ -4708,13 +4869,14 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) {
if (!m_gdb_comm.GetQXferFeaturesReadSupported())
return false;

// This holds register flags information for the whole of target.xml.
// These hold register type information for the whole of target.xml.
// target.xml may include further documents that
// GetGDBServerRegisterInfoXMLAndProcess will recurse to fetch and process.
// That's why we clear the cache here, and not in
// GetGDBServerRegisterInfoXMLAndProcess. To prevent it being cleared on every
// include read.
m_registers_flags_types.clear();
m_registers_enum_types.clear();
std::vector<DynamicRegisterInfo::Register> registers;
if (GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, "target.xml",
registers))
Expand Down
5 changes: 5 additions & 0 deletions lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,11 @@ class ProcessGDBRemote : public Process,
// entries are added. Which would invalidate any pointers set in the register
// info up to that point.
llvm::StringMap<std::unique_ptr<RegisterFlags>> m_registers_flags_types;

// Enum types are referenced by register fields. This does not store the data
// directly because the map may reallocate. Pointers to these are contained
// within instances of RegisterFlags.
llvm::StringMap<std::unique_ptr<FieldEnum>> m_registers_enum_types;
};

} // namespace process_gdb_remote
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ CompilerType RegisterTypeBuilderClang::GetRegisterType(
ScratchTypeSystemClang::GetForTarget(m_target);
assert(type_system);

std::string register_type_name = "__lldb_register_fields_";
register_type_name += name;
std::string register_type_name = "__lldb_register_fields_" + name;
// See if we have made this type before and can reuse it.
CompilerType fields_type =
type_system->GetTypeForIdentifier<clang::CXXRecordDecl>(
Expand All @@ -67,8 +66,43 @@ CompilerType RegisterTypeBuilderClang::GetRegisterType(
// We assume that RegisterFlags has padded and sorted the fields
// already.
for (const RegisterFlags::Field &field : flags.GetFields()) {
CompilerType field_type = field_uint_type;

if (const FieldEnum *enum_type = field.GetEnum()) {
const FieldEnum::Enumerators &enumerators = enum_type->GetEnumerators();
if (!enumerators.empty()) {
std::string enum_type_name =
"__lldb_register_fields_enum_" + enum_type->GetID();

// Enums can be used by mutiple fields and multiple registers, so we
// may have built this one already.
CompilerType field_enum_type =
type_system->GetTypeForIdentifier<clang::EnumDecl>(
enum_type_name);

if (field_enum_type)
field_type = field_enum_type;
else {
field_type = type_system->CreateEnumerationType(
enum_type_name, type_system->GetTranslationUnitDecl(),
OptionalClangModuleID(), Declaration(), field_uint_type, false);

type_system->StartTagDeclarationDefinition(field_type);

Declaration decl;
for (auto enumerator : enumerators) {
type_system->AddEnumerationValueToEnumerationType(
field_type, decl, enumerator.m_name.c_str(),
enumerator.m_value, byte_size * 8);
}

type_system->CompleteTagDeclarationDefinition(field_type);
}
}
}

type_system->AddFieldToRecordType(fields_type, field.GetName(),
field_uint_type, lldb::eAccessPublic,
field_type, lldb::eAccessPublic,
field.GetSizeInBits());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces
ScriptedPythonInterface.cpp
ScriptedProcessPythonInterface.cpp
ScriptedThreadPythonInterface.cpp
ScriptedThreadPlanPythonInterface.cpp
ScriptedPlatformPythonInterface.cpp

LINK_LIBS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include "../ScriptInterpreterPythonImpl.h"
#include "ScriptedPlatformPythonInterface.h"

#include "lldb/Target/ExecutionContext.h"

using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::python;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ StructuredData::DictionarySP ScriptedProcessPythonInterface::GetCapabilities() {
StructuredData::DictionarySP dict =
Dispatch<StructuredData::DictionarySP>("get_capabilities", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
error))
return {};

return dict;
Expand Down Expand Up @@ -90,7 +91,8 @@ StructuredData::DictionarySP ScriptedProcessPythonInterface::GetThreadsInfo() {
StructuredData::DictionarySP dict =
Dispatch<StructuredData::DictionarySP>("get_threads_info", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
error))
return {};

return dict;
Expand All @@ -106,7 +108,8 @@ bool ScriptedProcessPythonInterface::CreateBreakpoint(lldb::addr_t addr,
if (py_error.Fail())
error = py_error;

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};

return obj->GetBooleanValue();
Expand All @@ -131,7 +134,8 @@ lldb::offset_t ScriptedProcessPythonInterface::WriteMemoryAtAddress(
StructuredData::ObjectSP obj =
Dispatch("write_memory_at_address", py_error, addr, data_sp, error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return LLDB_INVALID_OFFSET;

// If there was an error on the python call, surface it to the user.
Expand All @@ -146,7 +150,8 @@ StructuredData::ArraySP ScriptedProcessPythonInterface::GetLoadedImages() {
StructuredData::ArraySP array =
Dispatch<StructuredData::ArraySP>("get_loaded_images", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, array, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, array,
error))
return {};

return array;
Expand All @@ -156,7 +161,8 @@ lldb::pid_t ScriptedProcessPythonInterface::GetProcessID() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_process_id", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return LLDB_INVALID_PROCESS_ID;

return obj->GetUnsignedIntegerValue(LLDB_INVALID_PROCESS_ID);
Expand All @@ -166,7 +172,8 @@ bool ScriptedProcessPythonInterface::IsAlive() {
Status error;
StructuredData::ObjectSP obj = Dispatch("is_alive", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};

return obj->GetBooleanValue();
Expand All @@ -177,7 +184,8 @@ ScriptedProcessPythonInterface::GetScriptedThreadPluginName() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_scripted_thread_plugin", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};

return obj->GetStringValue().str();
Expand All @@ -193,7 +201,8 @@ StructuredData::DictionarySP ScriptedProcessPythonInterface::GetMetadata() {
StructuredData::DictionarySP dict =
Dispatch<StructuredData::DictionarySP>("get_process_metadata", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
error))
return {};

return dict;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ ScriptedPythonInterface::ScriptedPythonInterface(
ScriptInterpreterPythonImpl &interpreter)
: ScriptedInterface(), m_interpreter(interpreter) {}

template <>
void ScriptedPythonInterface::ReverseTransform(
lldb_private::Stream *&original_arg, python::PythonObject transformed_arg,
Status &error) {
Stream *s = ExtractValueFromPythonObject<Stream *>(transformed_arg, error);
*original_arg = *s;
original_arg->PutCString(static_cast<StreamString *>(s)->GetData());
}

template <>
StructuredData::ArraySP
ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>(
Expand All @@ -48,12 +57,33 @@ Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>(
if (lldb::SBError *sb_error = reinterpret_cast<lldb::SBError *>(
python::LLDBSWIGPython_CastPyObjectToSBError(p.get())))
return m_interpreter.GetStatusFromSBError(*sb_error);
else
error.SetErrorString("Couldn't cast lldb::SBError to lldb::Status.");
error.SetErrorString("Couldn't cast lldb::SBError to lldb::Status.");

return {};
}

template <>
Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>(
python::PythonObject &p, Status &error) {
if (lldb::SBEvent *sb_event = reinterpret_cast<lldb::SBEvent *>(
python::LLDBSWIGPython_CastPyObjectToSBEvent(p.get())))
return m_interpreter.GetOpaqueTypeFromSBEvent(*sb_event);
error.SetErrorString("Couldn't cast lldb::SBEvent to lldb_private::Event.");

return nullptr;
}

template <>
Stream *ScriptedPythonInterface::ExtractValueFromPythonObject<Stream *>(
python::PythonObject &p, Status &error) {
if (lldb::SBStream *sb_stream = reinterpret_cast<lldb::SBStream *>(
python::LLDBSWIGPython_CastPyObjectToSBStream(p.get())))
return m_interpreter.GetOpaqueTypeFromSBStream(*sb_stream);
error.SetErrorString("Couldn't cast lldb::SBStream to lldb_private::Stream.");

return nullptr;
}

template <>
lldb::DataExtractorSP
ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
PythonObject::ResolveNameWithDictionary<python::PythonCallable>(
class_name, dict);
if (!init.IsAllocated())
return create_error(llvm::formatv("Could not find script class: %s",
return create_error(llvm::formatv("Could not find script class: {0}",
class_name.data()));

std::tuple<Args...> original_args = std::forward_as_tuple(args...);
Expand Down Expand Up @@ -248,8 +248,11 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
(PyObject *)m_object_instance_sp->GetValue());

if (!implementor.IsAllocated())
return ErrorWithMessage<T>(caller_signature,
"Python implementor not allocated.", error);
return llvm::is_contained(GetAbstractMethods(), method_name)
? ErrorWithMessage<T>(caller_signature,
"Python implementor not allocated.",
error)
: T{};

std::tuple<Args...> original_args = std::forward_as_tuple(args...);
auto transformed_args = TransformArgs(original_args);
Expand Down Expand Up @@ -322,6 +325,10 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
return python::SWIGBridge::ToSWIGWrapper(arg);
}

python::PythonObject Transform(lldb::ThreadPlanSP arg) {
return python::SWIGBridge::ToSWIGWrapper(arg);
}

python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) {
return python::SWIGBridge::ToSWIGWrapper(arg);
}
Expand All @@ -330,6 +337,14 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
return python::SWIGBridge::ToSWIGWrapper(arg);
}

python::PythonObject Transform(Event *arg) {
return python::SWIGBridge::ToSWIGWrapper(arg);
}

python::PythonObject Transform(Stream *arg) {
return python::SWIGBridge::ToSWIGWrapper(arg);
}

python::PythonObject Transform(lldb::DataExtractorSP arg) {
return python::SWIGBridge::ToSWIGWrapper(arg);
}
Expand Down Expand Up @@ -427,6 +442,14 @@ template <>
Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>(
python::PythonObject &p, Status &error);

template <>
Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>(
python::PythonObject &p, Status &error);

template <>
Stream *ScriptedPythonInterface::ExtractValueFromPythonObject<Stream *>(
python::PythonObject &p, Status &error);

template <>
lldb::BreakpointSP
ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//===-- ScriptedThreadPlanPythonInterface.cpp -----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "lldb/Host/Config.h"
#include "lldb/Utility/Log.h"
#include "lldb/lldb-enumerations.h"

#if LLDB_ENABLE_PYTHON

// LLDB Python header must be included first
#include "../lldb-python.h"

#include "../SWIGPythonBridge.h"
#include "../ScriptInterpreterPythonImpl.h"
#include "ScriptedThreadPlanPythonInterface.h"

using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::python;

ScriptedThreadPlanPythonInterface::ScriptedThreadPlanPythonInterface(
ScriptInterpreterPythonImpl &interpreter)
: ScriptedThreadPlanInterface(), ScriptedPythonInterface(interpreter) {}

llvm::Expected<StructuredData::GenericSP>
ScriptedThreadPlanPythonInterface::CreatePluginObject(
const llvm::StringRef class_name, lldb::ThreadPlanSP thread_plan_sp,
const StructuredDataImpl &args_sp) {
return ScriptedPythonInterface::CreatePluginObject(class_name, nullptr,
thread_plan_sp, args_sp);
}

llvm::Expected<bool>
ScriptedThreadPlanPythonInterface::ExplainsStop(Event *event) {
Status error;
StructuredData::ObjectSP obj = Dispatch("explains_stop", error, event);

if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error)) {
if (!obj)
return false;
return error.ToError();
}

return obj->GetBooleanValue();
}

llvm::Expected<bool>
ScriptedThreadPlanPythonInterface::ShouldStop(Event *event) {
Status error;
StructuredData::ObjectSP obj = Dispatch("should_stop", error, event);

if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error)) {
if (!obj)
return false;
return error.ToError();
}

return obj->GetBooleanValue();
}

llvm::Expected<bool> ScriptedThreadPlanPythonInterface::IsStale() {
Status error;
StructuredData::ObjectSP obj = Dispatch("is_stale", error);

if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error)) {
if (!obj)
return false;
return error.ToError();
}

return obj->GetBooleanValue();
}

lldb::StateType ScriptedThreadPlanPythonInterface::GetRunState() {
Status error;
StructuredData::ObjectSP obj = Dispatch("should_step", error);

if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return lldb::eStateStepping;

return static_cast<lldb::StateType>(obj->GetUnsignedIntegerValue(
static_cast<uint32_t>(lldb::eStateStepping)));
}

llvm::Expected<bool>
ScriptedThreadPlanPythonInterface::GetStopDescription(lldb_private::Stream *s) {
Status error;
Dispatch("stop_description", error, s);

if (error.Fail())
return error.ToError();

return true;
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//===-- ScriptedThreadPlanPythonInterface.h ---------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDTHREADPLANPYTHONINTERFACE_H
#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDTHREADPLANPYTHONINTERFACE_H

#include "lldb/Host/Config.h"

#if LLDB_ENABLE_PYTHON

#include "ScriptedPythonInterface.h"
#include "lldb/Interpreter/Interfaces/ScriptedThreadPlanInterface.h"
#include <optional>

namespace lldb_private {
class ScriptedThreadPlanPythonInterface : public ScriptedThreadPlanInterface,
public ScriptedPythonInterface {
public:
ScriptedThreadPlanPythonInterface(ScriptInterpreterPythonImpl &interpreter);

llvm::Expected<StructuredData::GenericSP>
CreatePluginObject(const llvm::StringRef class_name,
lldb::ThreadPlanSP thread_plan_sp,
const StructuredDataImpl &args_sp) override;

llvm::SmallVector<llvm::StringLiteral> GetAbstractMethods() const override {
return {};
}

llvm::Expected<bool> ExplainsStop(Event *event) override;

llvm::Expected<bool> ShouldStop(Event *event) override;

llvm::Expected<bool> IsStale() override;

lldb::StateType GetRunState() override;

llvm::Expected<bool> GetStopDescription(lldb_private::Stream *s) override;
};
} // namespace lldb_private

#endif // LLDB_ENABLE_PYTHON
#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDTHREADPLANPYTHONINTERFACE_H
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "lldb/Host/Config.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Utility/Log.h"
#include "lldb/lldb-enumerations.h"

Expand Down Expand Up @@ -44,7 +45,8 @@ lldb::tid_t ScriptedThreadPythonInterface::GetThreadID() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_thread_id", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return LLDB_INVALID_THREAD_ID;

return obj->GetUnsignedIntegerValue(LLDB_INVALID_THREAD_ID);
Expand All @@ -54,7 +56,8 @@ std::optional<std::string> ScriptedThreadPythonInterface::GetName() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_name", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};

return obj->GetStringValue().str();
Expand All @@ -64,7 +67,8 @@ lldb::StateType ScriptedThreadPythonInterface::GetState() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_state", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return eStateInvalid;

return static_cast<StateType>(obj->GetUnsignedIntegerValue(eStateInvalid));
Expand All @@ -74,7 +78,8 @@ std::optional<std::string> ScriptedThreadPythonInterface::GetQueue() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_queue", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};

return obj->GetStringValue().str();
Expand All @@ -85,7 +90,8 @@ StructuredData::DictionarySP ScriptedThreadPythonInterface::GetStopReason() {
StructuredData::DictionarySP dict =
Dispatch<StructuredData::DictionarySP>("get_stop_reason", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
error))
return {};

return dict;
Expand All @@ -96,7 +102,8 @@ StructuredData::ArraySP ScriptedThreadPythonInterface::GetStackFrames() {
StructuredData::ArraySP arr =
Dispatch<StructuredData::ArraySP>("get_stackframes", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr,
error))
return {};

return arr;
Expand All @@ -107,7 +114,8 @@ StructuredData::DictionarySP ScriptedThreadPythonInterface::GetRegisterInfo() {
StructuredData::DictionarySP dict =
Dispatch<StructuredData::DictionarySP>("get_register_info", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
error))
return {};

return dict;
Expand All @@ -117,7 +125,8 @@ std::optional<std::string> ScriptedThreadPythonInterface::GetRegisterContext() {
Status error;
StructuredData::ObjectSP obj = Dispatch("get_register_context", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
error))
return {};

return obj->GetAsString()->GetValue().str();
Expand All @@ -128,7 +137,8 @@ StructuredData::ArraySP ScriptedThreadPythonInterface::GetExtendedInfo() {
StructuredData::ArraySP arr =
Dispatch<StructuredData::ArraySP>("get_extended_info", error);

if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr, error))
if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr,
error))
return {};

return arr;
Expand Down
22 changes: 5 additions & 17 deletions lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,14 @@ class SWIGBridge {
static PythonObject ToSWIGWrapper(lldb::ExecutionContextRefSP ctx_sp);
static PythonObject ToSWIGWrapper(const TypeSummaryOptions &summary_options);
static PythonObject ToSWIGWrapper(const SymbolContext &sym_ctx);
static PythonObject ToSWIGWrapper(const Stream *stream);
static PythonObject ToSWIGWrapper(std::shared_ptr<lldb::SBStream> stream_sb);
static PythonObject ToSWIGWrapper(Event *event);

static PythonObject ToSWIGWrapper(lldb::ProcessAttachInfoSP attach_info_sp);
static PythonObject ToSWIGWrapper(lldb::ProcessLaunchInfoSP launch_info_sp);
static PythonObject ToSWIGWrapper(lldb::DataExtractorSP data_extractor_sp);

static PythonObject ToSWIGWrapper(std::unique_ptr<lldb::SBStream> stream_sb);
static PythonObject
ToSWIGWrapper(std::unique_ptr<lldb::SBStructuredData> data_sb);
static PythonObject
Expand All @@ -112,7 +114,6 @@ class SWIGBridge {

static python::ScopedPythonObject<lldb::SBCommandReturnObject>
ToSWIGWrapper(CommandReturnObject &cmd_retobj);
static python::ScopedPythonObject<lldb::SBEvent> ToSWIGWrapper(Event *event);
// These prototypes are the Pythonic implementations of the required
// callbacks. Although these are scripting-language specific, their definition
// depends on the public API.
Expand Down Expand Up @@ -147,21 +148,6 @@ class SWIGBridge {
const char *session_dictionary_name,
lldb::DebuggerSP debugger_sp);

static python::PythonObject LLDBSwigPythonCreateScriptedThreadPlan(
const char *python_class_name, const char *session_dictionary_name,
const StructuredDataImpl &args_data, std::string &error_string,
const lldb::ThreadPlanSP &thread_plan_sp);

static bool LLDBSWIGPythonCallThreadPlan(void *implementor,
const char *method_name,
lldb_private::Event *event_sp,
bool &got_error);

static bool LLDBSWIGPythonCallThreadPlan(void *implementor,
const char *method_name,
lldb_private::Stream *stream,
bool &got_error);

static python::PythonObject LLDBSwigPythonCreateScriptedBreakpointResolver(
const char *python_class_name, const char *session_dictionary_name,
const StructuredDataImpl &args, const lldb::BreakpointSP &bkpt_sp);
Expand Down Expand Up @@ -269,6 +255,8 @@ void *LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBLaunchInfo(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBError(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBEvent(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBStream(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBValue(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data);
} // namespace python
Expand Down
Loading