MicrosoftCXXABI.cpp revision 942f9fe11d3a9583eef6bc4ca2549b1f0d1694da
1071cc7deffad608165b1ddd5263e8bf181861520Charles Davis//===------- MicrosoftCXXABI.cpp - AST support for the Microsoft C++ ABI --===//
2071cc7deffad608165b1ddd5263e8bf181861520Charles Davis//
3071cc7deffad608165b1ddd5263e8bf181861520Charles Davis//                     The LLVM Compiler Infrastructure
4071cc7deffad608165b1ddd5263e8bf181861520Charles Davis//
5071cc7deffad608165b1ddd5263e8bf181861520Charles Davis// This file is distributed under the University of Illinois Open Source
6071cc7deffad608165b1ddd5263e8bf181861520Charles Davis// License. See LICENSE.TXT for details.
7071cc7deffad608165b1ddd5263e8bf181861520Charles Davis//
8071cc7deffad608165b1ddd5263e8bf181861520Charles Davis//===----------------------------------------------------------------------===//
9071cc7deffad608165b1ddd5263e8bf181861520Charles Davis//
10fc8f0e14ad142ed811e90fbd9a30e419e301c717Chris Lattner// This provides C++ AST support targeting the Microsoft Visual C++
11071cc7deffad608165b1ddd5263e8bf181861520Charles Davis// ABI.
12071cc7deffad608165b1ddd5263e8bf181861520Charles Davis//
13071cc7deffad608165b1ddd5263e8bf181861520Charles Davis//===----------------------------------------------------------------------===//
14071cc7deffad608165b1ddd5263e8bf181861520Charles Davis
15071cc7deffad608165b1ddd5263e8bf181861520Charles Davis#include "CXXABI.h"
1684e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner#include "clang/AST/Attr.h"
17071cc7deffad608165b1ddd5263e8bf181861520Charles Davis#include "clang/AST/ASTContext.h"
18071cc7deffad608165b1ddd5263e8bf181861520Charles Davis#include "clang/AST/DeclCXX.h"
19942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner#include "clang/AST/MangleNumberingContext.h"
20dae0cb52e4e3d46bbfc9a4510909522197a92e54Anders Carlsson#include "clang/AST/RecordLayout.h"
21dae0cb52e4e3d46bbfc9a4510909522197a92e54Anders Carlsson#include "clang/AST/Type.h"
22dae0cb52e4e3d46bbfc9a4510909522197a92e54Anders Carlsson#include "clang/Basic/TargetInfo.h"
23071cc7deffad608165b1ddd5263e8bf181861520Charles Davis
24071cc7deffad608165b1ddd5263e8bf181861520Charles Davisusing namespace clang;
25071cc7deffad608165b1ddd5263e8bf181861520Charles Davis
26071cc7deffad608165b1ddd5263e8bf181861520Charles Davisnamespace {
27942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner
28942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner/// \brief Numbers things which need to correspond across multiple TUs.
29942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner/// Typically these are things like static locals, lambdas, or blocks.
30942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Klecknerclass MicrosoftNumberingContext : public MangleNumberingContext {
31942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner  unsigned NumStaticLocals;
32942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner
33942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Klecknerpublic:
34942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner  MicrosoftNumberingContext() : NumStaticLocals(0) { }
35942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner
36942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner  /// Static locals are numbered by source order.
37942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner  virtual unsigned getManglingNumber(const VarDecl *VD) {
38942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner    assert(VD->isStaticLocal());
39942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner    return ++NumStaticLocals;
40942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner  }
41942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner};
42942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner
43071cc7deffad608165b1ddd5263e8bf181861520Charles Davisclass MicrosoftCXXABI : public CXXABI {
44071cc7deffad608165b1ddd5263e8bf181861520Charles Davis  ASTContext &Context;
45071cc7deffad608165b1ddd5263e8bf181861520Charles Davispublic:
46071cc7deffad608165b1ddd5263e8bf181861520Charles Davis  MicrosoftCXXABI(ASTContext &Ctx) : Context(Ctx) { }
47071cc7deffad608165b1ddd5263e8bf181861520Charles Davis
4884e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner  std::pair<uint64_t, unsigned>
4984e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner  getMemberPointerWidthAndAlign(const MemberPointerType *MPT) const;
50424ae9882e8a6eecc9dfe7c2d8623e72b2563873Charles Davis
518f88a1dcc57cfe8580eb1558a783ad8499bfe8e0Timur Iskhodzhanov  CallingConv getDefaultMethodCallConv(bool isVariadic) const {
52a83297bd83ba7184935dddde04787f88dd52e182Benjamin Kramer    if (!isVariadic &&
53a83297bd83ba7184935dddde04787f88dd52e182Benjamin Kramer        Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86)
54424ae9882e8a6eecc9dfe7c2d8623e72b2563873Charles Davis      return CC_X86ThisCall;
55a83297bd83ba7184935dddde04787f88dd52e182Benjamin Kramer    return CC_C;
56424ae9882e8a6eecc9dfe7c2d8623e72b2563873Charles Davis  }
57dae0cb52e4e3d46bbfc9a4510909522197a92e54Anders Carlsson
58dae0cb52e4e3d46bbfc9a4510909522197a92e54Anders Carlsson  bool isNearlyEmpty(const CXXRecordDecl *RD) const {
59dae0cb52e4e3d46bbfc9a4510909522197a92e54Anders Carlsson    // FIXME: Audit the corners
60dae0cb52e4e3d46bbfc9a4510909522197a92e54Anders Carlsson    if (!RD->isDynamicClass())
61dae0cb52e4e3d46bbfc9a4510909522197a92e54Anders Carlsson      return false;
62dae0cb52e4e3d46bbfc9a4510909522197a92e54Anders Carlsson
63dae0cb52e4e3d46bbfc9a4510909522197a92e54Anders Carlsson    const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
64dae0cb52e4e3d46bbfc9a4510909522197a92e54Anders Carlsson
65dae0cb52e4e3d46bbfc9a4510909522197a92e54Anders Carlsson    // In the Microsoft ABI, classes can have one or two vtable pointers.
665c3633fa57f27b0909ab5767715c4e66b8920165Ken Dyck    CharUnits PointerSize =
67bcfd1f55bfbb3e5944cd5e03d07b343e280838c4Douglas Gregor      Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0));
685c3633fa57f27b0909ab5767715c4e66b8920165Ken Dyck    return Layout.getNonVirtualSize() == PointerSize ||
695c3633fa57f27b0909ab5767715c4e66b8920165Ken Dyck      Layout.getNonVirtualSize() == PointerSize * 2;
70dae0cb52e4e3d46bbfc9a4510909522197a92e54Anders Carlsson  }
71942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner
72942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner  MangleNumberingContext *createMangleNumberingContext() const {
73942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner    return new MicrosoftNumberingContext();
74942f9fe11d3a9583eef6bc4ca2549b1f0d1694daReid Kleckner  }
75071cc7deffad608165b1ddd5263e8bf181861520Charles Davis};
76071cc7deffad608165b1ddd5263e8bf181861520Charles Davis}
77071cc7deffad608165b1ddd5263e8bf181861520Charles Davis
7884e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner// getNumBases() seems to only give us the number of direct bases, and not the
7984e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner// total.  This function tells us if we inherit from anybody that uses MI, or if
8084e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner// we have a non-primary base class, which uses the multiple inheritance model.
81a83297bd83ba7184935dddde04787f88dd52e182Benjamin Kramerstatic bool usesMultipleInheritanceModel(const CXXRecordDecl *RD) {
8284e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner  while (RD->getNumBases() > 0) {
8384e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    if (RD->getNumBases() > 1)
8484e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner      return true;
8584e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    assert(RD->getNumBases() == 1);
86a83297bd83ba7184935dddde04787f88dd52e182Benjamin Kramer    const CXXRecordDecl *Base =
87a83297bd83ba7184935dddde04787f88dd52e182Benjamin Kramer        RD->bases_begin()->getType()->getAsCXXRecordDecl();
8884e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    if (RD->isPolymorphic() && !Base->isPolymorphic())
8984e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner      return true;
9084e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    RD = Base;
9184e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner  }
9284e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner  return false;
9384e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner}
9484e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner
95a83297bd83ba7184935dddde04787f88dd52e182Benjamin Kramerstatic MSInheritanceModel MSInheritanceAttrToModel(attr::Kind Kind) {
964410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  switch (Kind) {
974410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  default: llvm_unreachable("expected MS inheritance attribute");
98cb428a1ac09dddf5ed3bf2a740f9ea31e9e11037Reid Kleckner  case attr::SingleInheritance:      return MSIM_Single;
99cb428a1ac09dddf5ed3bf2a740f9ea31e9e11037Reid Kleckner  case attr::MultipleInheritance:    return MSIM_Multiple;
100cb428a1ac09dddf5ed3bf2a740f9ea31e9e11037Reid Kleckner  case attr::VirtualInheritance:     return MSIM_Virtual;
101cb428a1ac09dddf5ed3bf2a740f9ea31e9e11037Reid Kleckner  case attr::UnspecifiedInheritance: return MSIM_Unspecified;
10284e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner  }
1034410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner}
10484e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner
1054410489163931892b568f0a43bd49c430a3aa3f5Reid KlecknerMSInheritanceModel CXXRecordDecl::getMSInheritanceModel() const {
106a83297bd83ba7184935dddde04787f88dd52e182Benjamin Kramer  if (Attr *IA = this->getAttr<MSInheritanceAttr>())
1074410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner    return MSInheritanceAttrToModel(IA->getKind());
1084410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  // If there was no explicit attribute, the record must be defined already, and
1094410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  // we can figure out the inheritance model from its other properties.
1104410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  if (this->getNumVBases() > 0)
111cb428a1ac09dddf5ed3bf2a740f9ea31e9e11037Reid Kleckner    return MSIM_Virtual;
1124410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  if (usesMultipleInheritanceModel(this))
113a3609b0c7685346308ed2c8022f94949bbfe7cdfReid Kleckner    return this->isPolymorphic() ? MSIM_MultiplePolymorphic : MSIM_Multiple;
114a3609b0c7685346308ed2c8022f94949bbfe7cdfReid Kleckner  return this->isPolymorphic() ? MSIM_SinglePolymorphic : MSIM_Single;
1154410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner}
1164410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner
1174410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner// Returns the number of pointer and integer slots used to represent a member
1184410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner// pointer in the MS C++ ABI.
1194410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//
1204410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner// Member function pointers have the following general form;  however, fields
1214410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner// are dropped as permitted (under the MSVC interpretation) by the inheritance
1224410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner// model of the actual class.
1234410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//
1244410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//   struct {
1254410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     // A pointer to the member function to call.  If the member function is
1264410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     // virtual, this will be a thunk that forwards to the appropriate vftable
1274410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     // slot.
1284410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     void *FunctionPointerOrVirtualThunk;
1294410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//
1304410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     // An offset to add to the address of the vbtable pointer after (possibly)
1314410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     // selecting the virtual base but before resolving and calling the function.
1324410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     // Only needed if the class has any virtual bases or bases at a non-zero
1334410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     // offset.
1344410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     int NonVirtualBaseAdjustment;
1354410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//
1364410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     // An offset within the vb-table that selects the virtual base containing
1374410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     // the member.  Loading from this offset produces a new offset that is
1384410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     // added to the address of the vb-table pointer to produce the base.
1394410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     int VirtualBaseAdjustmentOffset;
1404410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//
1414410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     // The offset of the vb-table pointer within the object.  Only needed for
1424410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//     // incomplete types.
143a3609b0c7685346308ed2c8022f94949bbfe7cdfReid Kleckner//     int VBPtrOffset;
1444410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner//   };
145a3609b0c7685346308ed2c8022f94949bbfe7cdfReid Klecknerstatic std::pair<unsigned, unsigned>
146a3609b0c7685346308ed2c8022f94949bbfe7cdfReid KlecknergetMSMemberPointerSlots(const MemberPointerType *MPT) {
147a3609b0c7685346308ed2c8022f94949bbfe7cdfReid Kleckner  const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl();
1484410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  MSInheritanceModel Inheritance = RD->getMSInheritanceModel();
1494410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  unsigned Ptrs;
1504410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  unsigned Ints = 0;
151a3609b0c7685346308ed2c8022f94949bbfe7cdfReid Kleckner  if (MPT->isMemberFunctionPointer()) {
15284e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    // Member function pointers are a struct of a function pointer followed by a
15384e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    // variable number of ints depending on the inheritance model used.  The
15484e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    // function pointer is a real function if it is non-virtual and a vftable
15584e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    // slot thunk if it is virtual.  The ints select the object base passed for
15684e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    // the 'this' pointer.
1574410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner    Ptrs = 1;  // First slot is always a function pointer.
15884e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    switch (Inheritance) {
159cb428a1ac09dddf5ed3bf2a740f9ea31e9e11037Reid Kleckner    case MSIM_Unspecified: ++Ints;  // VBTableOffset
160cb428a1ac09dddf5ed3bf2a740f9ea31e9e11037Reid Kleckner    case MSIM_Virtual:     ++Ints;  // VirtualBaseAdjustmentOffset
161a3609b0c7685346308ed2c8022f94949bbfe7cdfReid Kleckner    case MSIM_MultiplePolymorphic:
162cb428a1ac09dddf5ed3bf2a740f9ea31e9e11037Reid Kleckner    case MSIM_Multiple:    ++Ints;  // NonVirtualBaseAdjustment
163a3609b0c7685346308ed2c8022f94949bbfe7cdfReid Kleckner    case MSIM_SinglePolymorphic:
164cb428a1ac09dddf5ed3bf2a740f9ea31e9e11037Reid Kleckner    case MSIM_Single:      break;   // Nothing
16584e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    }
16684e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner  } else {
16784e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    // Data pointers are an aggregate of ints.  The first int is an offset
16884e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    // followed by vbtable-related offsets.
1694410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner    Ptrs = 0;
17084e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    switch (Inheritance) {
171cb428a1ac09dddf5ed3bf2a740f9ea31e9e11037Reid Kleckner    case MSIM_Unspecified: ++Ints;  // VBTableOffset
172cb428a1ac09dddf5ed3bf2a740f9ea31e9e11037Reid Kleckner    case MSIM_Virtual:     ++Ints;  // VirtualBaseAdjustmentOffset
173a3609b0c7685346308ed2c8022f94949bbfe7cdfReid Kleckner    case MSIM_MultiplePolymorphic:
174cb428a1ac09dddf5ed3bf2a740f9ea31e9e11037Reid Kleckner    case MSIM_Multiple:             // Nothing
175a3609b0c7685346308ed2c8022f94949bbfe7cdfReid Kleckner    case MSIM_SinglePolymorphic:
176cb428a1ac09dddf5ed3bf2a740f9ea31e9e11037Reid Kleckner    case MSIM_Single:      ++Ints;  // Field offset
17784e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner    }
17884e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner  }
1794410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  return std::make_pair(Ptrs, Ints);
1804410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner}
18184e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner
182a83297bd83ba7184935dddde04787f88dd52e182Benjamin Kramerstd::pair<uint64_t, unsigned> MicrosoftCXXABI::getMemberPointerWidthAndAlign(
183a83297bd83ba7184935dddde04787f88dd52e182Benjamin Kramer    const MemberPointerType *MPT) const {
1844410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  const TargetInfo &Target = Context.getTargetInfo();
1854410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  assert(Target.getTriple().getArch() == llvm::Triple::x86 ||
1864410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner         Target.getTriple().getArch() == llvm::Triple::x86_64);
1874410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  unsigned Ptrs, Ints;
188a3609b0c7685346308ed2c8022f94949bbfe7cdfReid Kleckner  llvm::tie(Ptrs, Ints) = getMSMemberPointerSlots(MPT);
1894410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  // The nominal struct is laid out with pointers followed by ints and aligned
1904410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  // to a pointer width if any are present and an int width otherwise.
1914410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  unsigned PtrSize = Target.getPointerWidth(0);
1924410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  unsigned IntSize = Target.getIntWidth();
1934410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  uint64_t Width = Ptrs * PtrSize + Ints * IntSize;
1944410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  unsigned Align = Ptrs > 0 ? Target.getPointerAlign(0) : Target.getIntAlign();
1954410489163931892b568f0a43bd49c430a3aa3f5Reid Kleckner  Width = llvm::RoundUpToAlignment(Width, Align);
19684e9ab44af3a16f66d62590505db2036ef0aa03bReid Kleckner  return std::make_pair(Width, Align);
197071cc7deffad608165b1ddd5263e8bf181861520Charles Davis}
198071cc7deffad608165b1ddd5263e8bf181861520Charles Davis
199071cc7deffad608165b1ddd5263e8bf181861520Charles DavisCXXABI *clang::CreateMicrosoftCXXABI(ASTContext &Ctx) {
200071cc7deffad608165b1ddd5263e8bf181861520Charles Davis  return new MicrosoftCXXABI(Ctx);
201071cc7deffad608165b1ddd5263e8bf181861520Charles Davis}
202071cc7deffad608165b1ddd5263e8bf181861520Charles Davis
203