aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/CIR/CodeGen
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/CIR/CodeGen')
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenClass.cpp2
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenCleanup.cpp6
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp30
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp16
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenFunction.h3
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.cpp60
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h109
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenValue.h7
8 files changed, 109 insertions, 124 deletions
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 9d12a13..8f4377b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -690,7 +690,7 @@ void CIRGenFunction::emitCXXAggrConstructorCall(
// every temporary created in a default argument expression is sequenced
// before the construction of the next array element, if any.
{
- assert(!cir::MissingFeatures::runCleanupsScope());
+ RunCleanupsScope scope(*this);
// Evaluate the constructor and its arguments in a regular
// partial-destroy cleanup.
diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
index 4d4d10b..8700697 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
@@ -28,6 +28,12 @@ using namespace clang::CIRGen;
// CIRGenFunction cleanup related
//===----------------------------------------------------------------------===//
+/// Emits all the code to cause the given temporary to be cleaned up.
+void CIRGenFunction::emitCXXTemporary(const CXXTemporary *temporary,
+ QualType tempType, Address ptr) {
+ pushDestroy(NormalAndEHCleanup, ptr, tempType, destroyCXXObject);
+}
+
//===----------------------------------------------------------------------===//
// EHScopeStack
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 60ccf18..901b937 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -46,6 +46,12 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
return dest;
}
+ void ensureDest(mlir::Location loc, QualType ty) {
+ if (!dest.isIgnored())
+ return;
+ dest = cgf.createAggTemp(ty, loc, "agg.tmp.ensured");
+ }
+
public:
AggExprEmitter(CIRGenFunction &cgf, AggValueSlot dest)
: cgf(cgf), dest(dest) {}
@@ -96,10 +102,22 @@ public:
Visit(die->getExpr());
}
void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *e) {
- assert(!cir::MissingFeatures::aggValueSlotDestructedFlag());
+ // Ensure that we have a slot, but if we already do, remember
+ // whether it was externally destructed.
+ bool wasExternallyDestructed = dest.isExternallyDestructed();
+ ensureDest(cgf.getLoc(e->getSourceRange()), e->getType());
+
+ // We're going to push a destructor if there isn't already one.
+ dest.setExternallyDestructed();
+
Visit(e->getSubExpr());
+
+ // Push that destructor we promised.
+ if (!wasExternallyDestructed)
+ cgf.emitCXXTemporary(e->getTemporary(), e->getType(), dest.getAddress());
}
void VisitLambdaExpr(LambdaExpr *e);
+ void VisitExprWithCleanups(ExprWithCleanups *e);
// Stubs -- These should be moved up when they are implemented.
void VisitCastExpr(CastExpr *e) {
@@ -241,11 +259,6 @@ public:
cgf.cgm.errorNYI(e->getSourceRange(),
"AggExprEmitter: VisitCXXStdInitializerListExpr");
}
-
- void VisitExprWithCleanups(ExprWithCleanups *e) {
- cgf.cgm.errorNYI(e->getSourceRange(),
- "AggExprEmitter: VisitExprWithCleanups");
- }
void VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *e) {
cgf.cgm.errorNYI(e->getSourceRange(),
"AggExprEmitter: VisitCXXScalarValueInitExpr");
@@ -588,6 +601,11 @@ void AggExprEmitter::VisitLambdaExpr(LambdaExpr *e) {
}
}
+void AggExprEmitter::VisitExprWithCleanups(ExprWithCleanups *e) {
+ CIRGenFunction::RunCleanupsScope cleanups(cgf);
+ Visit(e->getSubExpr());
+}
+
void AggExprEmitter::VisitCallExpr(const CallExpr *e) {
if (e->getCallReturnType(cgf.getContext())->isReferenceType()) {
cgf.cgm.errorNYI(e->getSourceRange(), "reference return type");
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 768d75d..5d3496a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -1099,15 +1099,17 @@ public:
CIRGenFunction::LexicalScope lexScope{cgf, loc,
b.getInsertionBlock()};
cgf.curLexScope->setAsTernary();
- b.create<cir::YieldOp>(loc, cgf.evaluateExprAsBool(e->getRHS()));
+ mlir::Value res = cgf.evaluateExprAsBool(e->getRHS());
+ lexScope.forceCleanup();
+ cir::YieldOp::create(b, loc, res);
},
/*falseBuilder*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
CIRGenFunction::LexicalScope lexScope{cgf, loc,
b.getInsertionBlock()};
cgf.curLexScope->setAsTernary();
- auto res = b.create<cir::ConstantOp>(loc, builder.getFalseAttr());
- b.create<cir::YieldOp>(loc, res.getRes());
+ auto res = cir::ConstantOp::create(b, loc, builder.getFalseAttr());
+ cir::YieldOp::create(b, loc, res.getRes());
});
return maybePromoteBoolResult(resOp.getResult(), resTy);
}
@@ -1143,15 +1145,17 @@ public:
CIRGenFunction::LexicalScope lexScope{cgf, loc,
b.getInsertionBlock()};
cgf.curLexScope->setAsTernary();
- auto res = b.create<cir::ConstantOp>(loc, builder.getTrueAttr());
- b.create<cir::YieldOp>(loc, res.getRes());
+ auto res = cir::ConstantOp::create(b, loc, builder.getTrueAttr());
+ cir::YieldOp::create(b, loc, res.getRes());
},
/*falseBuilder*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
CIRGenFunction::LexicalScope lexScope{cgf, loc,
b.getInsertionBlock()};
cgf.curLexScope->setAsTernary();
- b.create<cir::YieldOp>(loc, cgf.evaluateExprAsBool(e->getRHS()));
+ mlir::Value res = cgf.evaluateExprAsBool(e->getRHS());
+ lexScope.forceCleanup();
+ cir::YieldOp::create(b, loc, res);
});
return maybePromoteBoolResult(resOp.getResult(), resTy);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index dfd9d2c..cbc0f4a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1258,6 +1258,9 @@ public:
RValue emitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *expr);
+ void emitCXXTemporary(const CXXTemporary *temporary, QualType tempType,
+ Address ptr);
+
void emitCXXThrowExpr(const CXXThrowExpr *e);
void emitCtorPrologue(const clang::CXXConstructorDecl *ctor,
diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.cpp b/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.cpp
index bbc45e5..24a5fc2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.cpp
@@ -221,10 +221,9 @@ mlir::Value OpenACCRecipeBuilderBase::makeBoundsAlloca(
return initialAlloca;
}
-mlir::Value
-OpenACCRecipeBuilderBase::createBoundsLoop(mlir::Value subscriptedValue,
- mlir::Value bound,
- mlir::Location loc, bool inverse) {
+std::pair<mlir::Value, mlir::Value> OpenACCRecipeBuilderBase::createBoundsLoop(
+ mlir::Value subscriptedValue, mlir::Value subscriptedValue2,
+ mlir::Value bound, mlir::Location loc, bool inverse) {
mlir::Operation *bodyInsertLoc;
mlir::Type itrTy = cgf.cgm.convertType(cgf.getContext().UnsignedLongLongTy);
@@ -249,7 +248,6 @@ OpenACCRecipeBuilderBase::createBoundsLoop(mlir::Value subscriptedValue,
return cir::PtrStrideOp::create(builder, loc, eltLoad.getType(), eltLoad,
idxLoad);
-
};
auto forStmtBuilder = [&]() {
@@ -303,6 +301,8 @@ OpenACCRecipeBuilderBase::createBoundsLoop(mlir::Value subscriptedValue,
if (subscriptedValue)
subscriptedValue = doSubscriptOp(subscriptedValue, load);
+ if (subscriptedValue2)
+ subscriptedValue2 = doSubscriptOp(subscriptedValue2, load);
bodyInsertLoc = builder.createYield(loc);
},
/*stepBuilder=*/
@@ -325,7 +325,7 @@ OpenACCRecipeBuilderBase::createBoundsLoop(mlir::Value subscriptedValue,
// Leave the insertion point to be inside the body, so we can loop over
// these things.
builder.setInsertionPoint(bodyInsertLoc);
- return subscriptedValue;
+ return {subscriptedValue, subscriptedValue2};
}
mlir::acc::ReductionOperator
@@ -434,7 +434,7 @@ void OpenACCRecipeBuilderBase::createInitRecipe(
mlir::Location loc, mlir::Location locEnd, SourceRange exprRange,
mlir::Value mainOp, mlir::Region &recipeInitRegion, size_t numBounds,
llvm::ArrayRef<QualType> boundTypes, const VarDecl *allocaDecl,
- QualType origType) {
+ QualType origType, bool emitInitExpr) {
assert(allocaDecl && "Required recipe variable not set?");
CIRGenFunction::DeclMapRevertingRAII declMapRAII{cgf, allocaDecl};
@@ -464,14 +464,15 @@ void OpenACCRecipeBuilderBase::createInitRecipe(
// initialize this variable correctly.
CIRGenFunction::AutoVarEmission tempDeclEmission =
cgf.emitAutoVarAlloca(*allocaDecl, builder.saveInsertionPoint());
- cgf.emitAutoVarInit(tempDeclEmission);
+ if (emitInitExpr)
+ cgf.emitAutoVarInit(tempDeclEmission);
} else {
mlir::Value alloca = makeBoundsAlloca(
block, exprRange, loc, allocaDecl->getName(), numBounds, boundTypes);
// If the initializer is trivial, there is nothing to do here, so save
// ourselves some effort.
- if (allocaDecl->getInit() &&
+ if (emitInitExpr && allocaDecl->getInit() &&
(!cgf.isTrivialInitializer(allocaDecl->getInit()) ||
cgf.getContext().getLangOpts().getTrivialAutoVarInit() !=
LangOptions::TrivialAutoVarInitKind::Uninitialized))
@@ -484,35 +485,42 @@ void OpenACCRecipeBuilderBase::createInitRecipe(
void OpenACCRecipeBuilderBase::createFirstprivateRecipeCopy(
mlir::Location loc, mlir::Location locEnd, mlir::Value mainOp,
- CIRGenFunction::AutoVarEmission tempDeclEmission,
- mlir::acc::FirstprivateRecipeOp recipe, const VarDecl *varRecipe,
- const VarDecl *temporary) {
- mlir::Block *block =
- createRecipeBlock(recipe.getCopyRegion(), mainOp.getType(), loc,
- /*numBounds=*/0, /*isInit=*/false);
- builder.setInsertionPointToEnd(&recipe.getCopyRegion().back());
+ const VarDecl *allocaDecl, const VarDecl *temporary,
+ mlir::Region &copyRegion, size_t numBounds) {
+ mlir::Block *block = createRecipeBlock(copyRegion, mainOp.getType(), loc,
+ numBounds, /*isInit=*/false);
+ builder.setInsertionPointToEnd(&copyRegion.back());
CIRGenFunction::LexicalScope ls(cgf, loc, block);
- mlir::BlockArgument fromArg = block->getArgument(0);
- mlir::BlockArgument toArg = block->getArgument(1);
+ mlir::Value fromArg = block->getArgument(0);
+ mlir::Value toArg = block->getArgument(1);
- mlir::Type elementTy =
- mlir::cast<cir::PointerType>(mainOp.getType()).getPointee();
+ llvm::MutableArrayRef<mlir::BlockArgument> boundsRange =
+ block->getArguments().drop_front(2);
- // Set the address of the emission to be the argument, so that we initialize
- // that instead of the variable in the other block.
- tempDeclEmission.setAllocatedAddress(
- Address{toArg, elementTy, cgf.getContext().getDeclAlign(varRecipe)});
+ for (mlir::BlockArgument boundArg : llvm::reverse(boundsRange))
+ std::tie(fromArg, toArg) =
+ createBoundsLoop(fromArg, toArg, boundArg, loc, /*inverse=*/false);
+
+ // Set up the 'to' address.
+ mlir::Type elementTy =
+ mlir::cast<cir::PointerType>(toArg.getType()).getPointee();
+ CIRGenFunction::AutoVarEmission tempDeclEmission(*allocaDecl);
tempDeclEmission.emittedAsOffload = true;
+ tempDeclEmission.setAllocatedAddress(
+ Address{toArg, elementTy, cgf.getContext().getDeclAlign(allocaDecl)});
+ // Set up the 'from' address from the temporary.
CIRGenFunction::DeclMapRevertingRAII declMapRAII{cgf, temporary};
cgf.setAddrOfLocalVar(
temporary,
- Address{fromArg, elementTy, cgf.getContext().getDeclAlign(varRecipe)});
-
+ Address{fromArg, elementTy, cgf.getContext().getDeclAlign(allocaDecl)});
cgf.emitAutoVarInit(tempDeclEmission);
+
+ builder.setInsertionPointToEnd(&copyRegion.back());
mlir::acc::YieldOp::create(builder, locEnd);
}
+
// This function generates the 'combiner' section for a reduction recipe. Note
// that this function is not 'insertion point' clean, in that it alters the
// insertion point to be inside of the 'combiner' section of the recipe, but
diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h b/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h
index 21707ad..a5da744 100644
--- a/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h
+++ b/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h
@@ -49,14 +49,16 @@ protected:
// Creates a loop through an 'acc.bounds', leaving the 'insertion' point to be
// the inside of the loop body. Traverses LB->UB UNLESS `inverse` is set.
// Returns the 'subscriptedValue' changed with the new bounds subscript.
+ std::pair<mlir::Value, mlir::Value>
+ createBoundsLoop(mlir::Value subscriptedValue, mlir::Value subscriptedValue2,
+ mlir::Value bound, mlir::Location loc, bool inverse);
+
mlir::Value createBoundsLoop(mlir::Value subscriptedValue, mlir::Value bound,
- mlir::Location loc, bool inverse);
+ mlir::Location loc, bool inverse) {
+ return createBoundsLoop(subscriptedValue, {}, bound, loc, inverse).first;
+ }
+
mlir::acc::ReductionOperator convertReductionOp(OpenACCReductionOperator op);
- void createFirstprivateRecipeCopy(
- mlir::Location loc, mlir::Location locEnd, mlir::Value mainOp,
- CIRGenFunction::AutoVarEmission tempDeclEmission,
- mlir::acc::FirstprivateRecipeOp recipe, const VarDecl *varRecipe,
- const VarDecl *temporary);
// This function generates the 'combiner' section for a reduction recipe. Note
// that this function is not 'insertion point' clean, in that it alters the
@@ -66,11 +68,19 @@ protected:
mlir::Value mainOp,
mlir::acc::ReductionRecipeOp recipe,
size_t numBounds);
+
void createInitRecipe(mlir::Location loc, mlir::Location locEnd,
SourceRange exprRange, mlir::Value mainOp,
mlir::Region &recipeInitRegion, size_t numBounds,
llvm::ArrayRef<QualType> boundTypes,
- const VarDecl *allocaDecl, QualType origType);
+ const VarDecl *allocaDecl, QualType origType,
+ bool emitInitExpr);
+
+ void createFirstprivateRecipeCopy(mlir::Location loc, mlir::Location locEnd,
+ mlir::Value mainOp,
+ const VarDecl *allocaDecl,
+ const VarDecl *temporary,
+ mlir::Region &copyRegion, size_t numBounds);
void createRecipeDestroySection(mlir::Location loc, mlir::Location locEnd,
mlir::Value mainOp, CharUnits alignment,
@@ -150,63 +160,6 @@ class OpenACCRecipeBuilder : OpenACCRecipeBuilderBase {
return recipeName;
}
- // Create the 'init' section of the recipe, including the 'copy' section for
- // 'firstprivate'. Note that this function is not 'insertion point' clean, in
- // that it alters the insertion point to be inside of the 'destroy' section of
- // the recipe, but doesn't restore it aftewards.
- void createRecipeInitCopy(mlir::Location loc, mlir::Location locEnd,
- SourceRange exprRange, mlir::Value mainOp,
- RecipeTy recipe, const VarDecl *varRecipe,
- const VarDecl *temporary) {
- // TODO: OpenACC: when we get the 'pointer' variants for
- // firstprivate/reduction, this probably should be removed/split into
- // functions for the BuilderBase.
- assert(varRecipe && "Required recipe variable not set?");
-
- CIRGenFunction::AutoVarEmission tempDeclEmission{
- CIRGenFunction::AutoVarEmission::invalid()};
- CIRGenFunction::DeclMapRevertingRAII declMapRAII{cgf, varRecipe};
-
- // Do the 'init' section of the recipe IR, which does an alloca, then the
- // initialization (except for firstprivate).
- mlir::Block *block =
- createRecipeBlock(recipe.getInitRegion(), mainOp.getType(), loc,
- /*numBounds=*/0, /*isInit=*/true);
- builder.setInsertionPointToEnd(&recipe.getInitRegion().back());
- CIRGenFunction::LexicalScope ls(cgf, loc, block);
-
- tempDeclEmission =
- cgf.emitAutoVarAlloca(*varRecipe, builder.saveInsertionPoint());
-
- // 'firstprivate' doesn't do its initialization in the 'init' section,
- // instead it does it in the 'copy' section. SO, only do 'init' here for
- // reduction.
- if constexpr (std::is_same_v<RecipeTy, mlir::acc::ReductionRecipeOp>) {
- // Unlike Private, the recipe here is always required as it has to do
- // init, not just 'default' init.
- if (!varRecipe->getInit())
- cgf.cgm.errorNYI(exprRange, "reduction init recipe");
- cgf.emitAutoVarInit(tempDeclEmission);
- }
-
- mlir::acc::YieldOp::create(builder, locEnd);
-
- if constexpr (std::is_same_v<RecipeTy, mlir::acc::FirstprivateRecipeOp>) {
- if (!varRecipe->getInit()) {
- // If we don't have any initialization recipe, we failed during Sema to
- // initialize this correctly. If we disable the
- // Sema::TentativeAnalysisScopes in SemaOpenACC::CreateInitRecipe, it'll
- // emit an error to tell us. However, emitting those errors during
- // production is a violation of the standard, so we cannot do them.
- cgf.cgm.errorNYI(
- exprRange, "firstprivate copy-init recipe not properly generated");
- }
-
- createFirstprivateRecipeCopy(loc, locEnd, mainOp, tempDeclEmission,
- recipe, varRecipe, temporary);
- }
- }
-
public:
OpenACCRecipeBuilder(CIRGen::CIRGenFunction &cgf,
CIRGen::CIRGenBuilderTy &builder)
@@ -221,19 +174,6 @@ public:
BuiltinType::ArraySection) &&
"array section shouldn't make it to recipe creation");
- // TODO: OpenACC: This is a bit of a hackery to get this to not change for
- // the non-private recipes. This will be removed soon, when we get this
- // 'right' for firstprivate and reduction.
- if constexpr (std::is_same_v<RecipeTy, mlir::acc::FirstprivateRecipeOp>) {
- if (numBounds) {
- cgf.cgm.errorNYI(varRef->getSourceRange(),
- "firstprivate-init with bounds");
- }
- boundTypes = {};
- numBounds = 0;
- origType = baseType;
- }
-
mlir::ModuleOp mod = builder.getBlock()
->getParent()
->template getParentOfType<mlir::ModuleOp>();
@@ -262,21 +202,20 @@ public:
if constexpr (std::is_same_v<RecipeTy, mlir::acc::PrivateRecipeOp>) {
createInitRecipe(loc, locEnd, varRef->getSourceRange(), mainOp,
recipe.getInitRegion(), numBounds, boundTypes, varRecipe,
- origType);
+ origType, /*emitInitExpr=*/true);
} else if constexpr (std::is_same_v<RecipeTy,
mlir::acc::ReductionRecipeOp>) {
createInitRecipe(loc, locEnd, varRef->getSourceRange(), mainOp,
recipe.getInitRegion(), numBounds, boundTypes, varRecipe,
- origType);
+ origType, /*emitInitExpr=*/true);
createReductionRecipeCombiner(loc, locEnd, mainOp, recipe, numBounds);
} else {
static_assert(std::is_same_v<RecipeTy, mlir::acc::FirstprivateRecipeOp>);
- // TODO: OpenACC: we probably want this to call createInitRecipe as well,
- // but do so in a way that omits the 'initialization', so that we can do
- // it separately, since it belongs in the 'copy' region. It also might
- // need a way of getting the tempDeclEmission out of it for that purpose.
- createRecipeInitCopy(loc, locEnd, varRef->getSourceRange(), mainOp,
- recipe, varRecipe, temporary);
+ createInitRecipe(loc, locEnd, varRef->getSourceRange(), mainOp,
+ recipe.getInitRegion(), numBounds, boundTypes, varRecipe,
+ origType, /*emitInitExpr=*/false);
+ createFirstprivateRecipeCopy(loc, locEnd, mainOp, varRecipe, temporary,
+ recipe.getCopyRegion(), numBounds);
}
if (origType.isDestructedType())
diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h
index ea8625a..25b6ecb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenValue.h
+++ b/clang/lib/CIR/CodeGen/CIRGenValue.h
@@ -371,6 +371,13 @@ public:
mayOverlap, isZeroed);
}
+ IsDestructed_t isExternallyDestructed() const {
+ return IsDestructed_t(destructedFlag);
+ }
+ void setExternallyDestructed(bool destructed = true) {
+ destructedFlag = destructed;
+ }
+
clang::Qualifiers getQualifiers() const { return quals; }
Address getAddress() const { return addr; }