diff --git a/doc/cn/CPPCodingTalkAboutPointer.md b/doc/cn/CPPCodingTalkAboutPointer.md new file mode 100644 index 0000000000000000000000000000000000000000..8a57886e691dbd26f3141852e33b450c1532838e --- /dev/null +++ b/doc/cn/CPPCodingTalkAboutPointer.md @@ -0,0 +1,266 @@ +# C++编程探讨之指针 + +# 背景 + +`C/C++`中指针的使用具有极大的灵活性,伴随着的是更多的安全风险,同时这也对程序员提出了更高的要求。本文将讨论裸指针在`C/C++`中当如何被使用,乃至最终确立一种编码范式。 + +# 裸指针vs引用 + +## 成员访问 + +当访问对象成员时,裸指针存在为空的场景(指针的有效性由闭合对象或函数从逻辑上自保证),所以必须检查非法指针。而引用必定非空。 + +## 做容器成员 + +引用从`C++`语义中,表达的是别名关系,理论上不占内存(实际中规中矩的编译器对于引用的内部实现是指针)。引用本身不是对象,这点与指针不同,指针可以作为各容器成员,而引用不行。 + +# 裸指针vs智能指针 + +## 堆对象销毁 + +```c++ +class Int { + ... +private: + int data; +} + +void test(int* in) { + Int* tmp = new Int(); + ... + goto LABEL; + ... + + delete tmp; +LABEL: +} +``` + +对于资源(堆对象、栈对象、文件资源等)的使用,遵循**“谁申请,谁释放”**的原则(RAII),这样可以最大限度的降低资源泄露的可能。 + +裸指针的`new`与`delete`之间往往会包含一段处理逻辑以及子函数调用,中间的处理逻辑可能发生异常、跳转等动作(中间的处理逻辑的行为不会由当前对象越权限制,超出`new`行为的管辖范围),而跳过资源的释放,从而造成资源泄露(如示例中`test`函数中`tmp`对象)。 + +智能指针改造为`auto tmp = std::make_unique();`,构造对象`tmp`时,即绑定其`delete`行为,退出当前作用域销毁,而避免了资源泄露的可能。 + +## 管理权vs使用权 + +```c++ +int* delete(int* in); +``` + +管理权:拥有销毁、重建对象的权利 + +使用权:拥有访问、修改对象的权利 + +如上示例所示,当使用裸指针传递参数时,由于其隐含了转移所有权的属性(可能转移所有权,亦可能没有),入参`in`以及出参均无法确定行使了**管理权**还是**使用权**。调用此函数将需要额外补充信息:`in`是否会被`delete`函数销毁?返回值是否需要调用者销毁? + +```c++ +std::unique_ptr delete(std::unique_ptr& in); +``` + +使用智能指针将在接口中明确表达参数的角色,如`std::unique_ptr& in`代表`delete`函数享有其**使用权**,函数返回值代表`delete`函数转移所有权。 + +# 指针使用范式 + +## `new`创建的对象,必须立即绑定其销毁方式 + +错误示例: + +```c++ +Object* obj = new Object(); +... +delete obj; +``` + +正确示例: + +```c++ +std::unique_ptr obj = std::make_unique(new Object()); +``` + +## 申请的资源,必须立即绑定其释放方式 + +错误示例: + +```c++ +FILE* file = open("xxx.txt"); +... +file->close(); +``` + +正确示例(本例比较通用,最佳方式应用类封装`open`): + +```c++ +template +class ResourceGuard { +public: + ResourceGuard(T* _obj, Func _func) : obj(_obj), func(_func) {} + + ~ResourceGuard() { obj.func(); } +private: + T* obj; + Func func; +} + +FILE* file = open("xxx.txt"); +auto fileGuard = ResourceGuard>(file, FILE::close); +... +``` + +## 确定不为空的场景,使用引用而非指针 + +错误示例: + +```c++ +void func1(int* in) { + if (in == nullptr) return; + ... +} + +void func2() { + int* p = nullptr; + ... + if (p != nullptr) { + func1(p); + } +} +``` + +正确示例: + +```c++ +void func1(int& in) { + ... +} + +void func2() { + int* p = nullptr; + ... + if (p != nullptr) { + func1(*p); + } +} +``` + +## 作为容器成员(不具管理权),确定不为空时,使用封装的引用容器,而非指针 + +错误示例: + +```c++ +void func(std::vector& in) { + for (auto *p : in) { + if (p == nullptr) { + continue; + } + ... + } +} +``` + +正确示例: + +```c++ +template +class Ref { +public: + Ref() = delete; + Ref(T& ref) : data(&ref) {} + + ... + + operator T() const noexcept { + return *data; + } + +private: + T* data; +} + +template +using ref_vector = std::vector>; +void func(ref_vector& in) { + for (auto p : in) { + int& data = p; + ... + } +} +``` + +## 作为容器成员(具备管理权),使用具有管理生命周期的容器,而非指针容器 + +错误示例: + +```c++ +std::vector data; +... +for (auto *p : data) { + delete p; +} +``` + +正确示例: + +```c++ +template +class ptr_vector { +public: + ~ptr_vector() { + for (auto *p : data) { + delete p; + } + } + +private: + std::vector data; +} + +ptr_vector data; +... +``` + +## 显示转移对象管理权,明确对象使用权 + +`C++11`新增了`move`语义,并废弃`auto_ptr`而使用需显示转移所有权的`unique_ptr`,使得栈对象和堆对象的生命周期管理方式可以进行统一。 + +栈对象转移示例: + +```c++ +std::vector func() { + std::vector data; + data.push_back(0); + return std::move(data); +} +``` + +模糊的堆对象转移示例: + +```c++ +Object* func() { + std::unique_ptr data = std::make_unique(new Object); + Object& rData = ToRef(data); + rData.push_back(0); + return data.release(); +} +``` + +明晰的的堆对象转移示例: + +```c++ +std::unique_ptr func() { + std::unique_ptr data = std::make_unique(new Object); + Object& rData = ToRef(data); + rData.push_back(0); + return std::move(data); +} +``` + +## 应当使用指针场景 + +1. 第三方库函数传入或传出指针,但必须在调用前一刻使用`unique_ptr.get()`或`unique_ptr.release()`构建入参,出参也必须在拿到后立即使用`unique_ptr`接住或判空并转引用。 +2. 作为容器成员(不具管理权),使用场景中有空指针设计,但必须在使用前立即判空并转引用,不支持指针扩散。 + +# 备注 + +上述的`Ref`、`ref_vector`已开发完成,`Ref`由于`operator.`无法被重载,所以定义为`SafePtr`。 + +上述的`ResourceGuard`、`ptr_vector`正在开发中,文中主要为示意。 diff --git a/doc/cn/DeveloperGuide4Utility.md b/doc/cn/DeveloperGuide4Utility.md new file mode 100644 index 0000000000000000000000000000000000000000..c4d4710a26b55f537f8eb2ad941d7cdc2bd882d8 --- /dev/null +++ b/doc/cn/DeveloperGuide4Utility.md @@ -0,0 +1,431 @@ +# Maple通用模块应用手册 + +# Cast + +## `instance_of`与`safe_cast` + +`maple`对于`C++`的使用,原则上需要禁用`RTTI`,即禁用`dynamic_cast`。而由于编译器系统的复杂性,完全通过类设计来破除父类向子类转换的情况,反而会使得对象关系变得更加复杂,得不偿失。所以`maple`代码实现中便有了众多如下设计: + +```c++ +SubClass *sub = nullptr; +if (base.Type() == SubType) { + sub = static_cast(base); +} +``` + +通过设计某属性字段来实现父类与子类之间的关系绑定,从而达到与`dynamic_cast`相同的效果。 + +但此种写法仍有一些不足,首先`SubType`与`SubClass`之间隐藏有静态绑定关系,这由设计者决定,却需要调用者将两者关系显化,这会产生强依赖;其次,并不是所有场景都如类型比较这样直观,复杂的场景对于调用者来说更容易出错;最后,散落在各地的转换,将会埋下散弹式修改的问题。所以我们设计出了`safe_cast`,由设计者注册转换关系,调用者只需以`dynamic_cast`的方式调用即可 + +### 注册方式 + +通过`REGISTER_SAFE_CAST`宏来达成注册,声明如下: + +```c++ +#define REGISTER_SAFE_CAST(type, condition) +``` + +其中`type`为子类类型(假设为`B`),`condition`为匹配到`B`以及其子类所有场景的布尔表达式。示例如下: + +```c++ +class A; +class B : A; +class C : B; +REGISTER_SAFE_CAST(B, from.Kind() == B || from.Kind() == C); +REGISTER_SAFE_CAST(C, from.Kind() == C); +``` + +`from`为表达式传入的类型的形参名。 + +*注意:* + +*- 注册同时持了子类向父类转换以及父类向子类转换。* + +*- `condition`可以是任意的布尔表达式,但设计者应尽量使其符合继承关系,避免非继承关系的转换。* + +*- 对于复杂的`condition`使用`kind ==`表示一棵继承关系树是不明智的,需考虑优化,如:组织`kind`的范围、使用特定位标记等方式来达成快速匹配。* + +### 使用场景 + +1. 针对只做单个类型匹配的场景 + +```c++ +SubClass *sub = safe_cast(base); +if (sub == nullptr) { + // TODO +} +``` + +2. 针对多个类型匹配的场景 + +```c++ +if (instanceof(base)) { + auto *sub = static_cast(base); + // TODO +} else if (instanceof(base)) { + auto *sub = static_cast(base); + // TODO +} +... +``` + +*注意:* + +*- 对于`switch case`等已经正确识别类型的场景,使用`static_cast`即可。* + +*- `safe_cast`返回值永远是指针,外部识别是否转换成功。* + +*- 输入若为指针,`instance_of`与`safe_cast`都会做判空,所以`base`非空时优先传入引用* + +# Container + +## `Ptr` + +`Ptr`模拟了原生指针的行为,但切除了对数组操作的支持。 + +其通过在构造、赋值的操作中校验数据,使得指针对象仅在构造时需验证满足某特征,而传递和使用时均无需再次验证该特征,降低重复检查的开销。 + +```c++ +template +using PtrCheckerType = void (*)(const T*); + +template +constexpr void CheckNothing(const T*) {} + +template Check = CheckNothing> +class Ptr; +``` + +如上所示,`Ptr`的默认校验行为不做任何检查,通过 + +```c++ +template < typename T> +using XXXPtr = utils::Ptr>; +``` + +即可定义不同场景下的已经校验过的指针对象,统称安全指针。 + +*注:其由`safe_ptr`的诉求而扩展而来,但`safe_ptr`相对通用的`Ptr`场景更为复杂,因为其针对的是指针本身的合法性而非指针对象的特征。* + +## `safe_ptr` + +参见《CPPCodingTalkAboutPointer》中`Ref`的定义。由于`c++`中`operator.`无法进行重载,所以无法构建`Ref`对象,但可以定义与其等效的非空指针对象,即`safe_ptr`。 + +### 场景:数组、字典等容器成员 + +示例可见于《CPPCodingTalkAboutPointer》。 + +为了弥补`operator.`无法重载的问题,后续将扩展`Utility`中`ToRef`小工具的能力,保证指针无开销但安全转换为引用。其可能特化如下: + +```c++ +template +constexpr T &ToRef(safe_ptr ptr) { + return *ptr; +} +``` + +*注:对于使用频繁的容器,后续将封装`ref_xxx`系列的容器以取代`safe_ptr`这类使用场景。* + +### 场景:作为对象成员 + +此为`safe_ptr`开发出来后意外发掘的场景。 + +由于存在引用数据成员的类,编译器无法为其生成默认拷贝和转移,但很多场景下需要支持拷贝和转移能力,此时通常的做法是存储为指针成员。但指针成员将带来两个问题: + +1. 针对指针成员访问时需要去识别指针的合法性。 +2. 重构和演进都有可能从定义成员的角色方来改变其行为可空,而使用成员的角色方不一定会被告知,这可能会导致进一步的隐藏bug。 + +而使用`safe_ptr`代替裸指针,`safe_ptr`解引用处都使用`ToRef`来转引用(裸指针的`ToRef`有额外的开销),将会避免这样的问题。 + +*注意:`safe_ptr`设计为容器或对象成员,而不应用于函数传参,函数传参应使用`&`。* + +## `mpl_iterator` + +`mpl_iterator`原名为`iterator`,但由于和容器中`using iterator`重名,所以添加`mpl`前缀。 + +由于`ref_xxx`容器的设计,以及未来可能扩展small talk系列,重复的迭代器设计是个比较麻烦的问题,所以抽象出持续演进的统一迭代器容器,通过`mpl_iterator_traits`进行少量配置而快速实现新容器的迭代器。 + +当前基于`ref_vector`的迭代器诉求,设计`mpl_iterator_traits`的成员如下: + +```c++ +template +struct mpl_iterator_traits { + using iterator_category = typename std::iterator_traits::iterator_category; + using value_type = typename std::iterator_traits::value_type; + using difference_type = typename std::iterator_traits::difference_type; + using pointer = typename std::iterator_traits::pointer; + using reference = typename std::iterator_traits::reference; + + static reference operator_dereference(Iterator iter) { + return *iter; + }; + + static Iterator operator_arrow(Iterator iter) { + return iter; + } + + static reference operator_bracket(Iterator iter, difference_type n) { + return iter[n]; + } +}; + +``` + +## `ref_vector` + +参见《CPPCodingTalkAboutPointer》中`ref_vector`的定义。 + +使用指针数组:`std::vector` + +```c++ + int a = 0, b = 1; + + std::vector data; + data.push_back(&a); + data.push_back(&b); + ASSERT_EQ(*data[0], 0); + ASSERT_EQ(*data[1], 1); +``` + +重构为引用数组:`utils::ref_vector` +```c++ + int a = 0, b = 1; + + ref_vector data; + data.push_back(a); + data.push_back(b); + ASSERT_EQ(data[0], 0); + ASSERT_EQ(data[1], 1); +``` + +## `Index` + +`Index`的设计初衷为满足基础类型的静态安全。 + +如`GStrIdx`、`UStrIdx`、`U16StrIdx`三者在业务代码中会共同参与计算,包括其提供的接口也有比较高的相似性。它们底层均为`uint32`,若均定义为`uint32`那将是个灾难,调用者不得不得小心翼翼,但也很难避免传错数据。所以使静态类型互斥,由编译器来检查使用的正确性,将大大降低出错的几率。 + +定义一个静态类型方式非常简单,只要定义不同的Tag即可,如下: + +```c++ +class GStrTag; +using GStrIdx = utils::Index; + +class UStrTag; +using UStrIdx = utils::Index; + +class U16StrTag; +using U16StrIdx = utils::Index; +``` + +# Generalize Pattern + +## `ObjectFactory` + +`ObjectFactory`为针对抽象工厂的封装,用于解决以下问题: + +1. 消除代码中由`switch ... case`、`if ... else if ...`等构成的具有高圈复杂度的大函数,这类函数难以维护和扩展,且灵活度低。*(注意:应避免机械的性解决问题,需分析语义做好设计,如配合模板模式等,实现设计隔离而非仅仅代码隔离。)* + +2. 采用工厂将依赖反转,更容易将调用者和设计者、设计者和设计者之间隔离。 + +其应用方式如下示例: + +```c++ +// 定义Key,用于标记工厂将创建的产品类别 +enum class ObjectType { + kPlus, + kMinus +}; + +// 定义接口,所有产品应实现的接口协议 +class Base { + public: + virtual ~Base() = default; + virtual int32_t DoIt(int32_t lhs, int32_t rhs) const = 0; +}; + +// 定义工厂,使用ObjectType作为查找关键字,Base为接口协议,int32_t为所有产品构造函数的参数类型 +using TestObjectFactory = ObjectFactory; + +// Key,接口,工厂需要对注册者和调用者均可见,所以可能在.h文件中,可能在.cpp文件汇总 +// 产品只要保证能注册入工厂,可以在不同的.cpp文件中 +// 定义产品 +class ObjectPlus : public Base { + public: + // 定义工厂时所约定的构造函数 + explicit ObjectPlus(int32_t base) : base(base) {} + virtual ~ObjectPlus() = default; + + // 定义接口时的约定 + virtual int32_t DoIt(int32_t lhs, int32_t rhs) const override { + return base + lhs + rhs; + } + + private: + int32_t base; +}; + +// 定义产品,可能在另外的.cpp文件中 +class ObjectMinus : public Base { + public: + explicit ObjectMinus(int32_t base) : base(base) {} + virtual ~ObjectMinus() = default; + + virtual int32_t DoIt(int32_t lhs, int32_t rhs) const override { + return base + lhs - rhs; + } + + private: + int32_t base; +}; + +// 注册产品,注册产品的方式随着Key,接口,工厂,产品之间的分布以及加载可灵活处理,保证注册成功即可 +// 此处利用static变量初始化来确保注册 +bool RegisterObject() { + RegisterFactoryObject(); + RegisterFactoryObject(); +} +static auto testObjectFactory = RegisterObject(); + +TEST(TestFactory, ObjectFactory) { + // 获取产品对象,需要判空,本示例略 + auto obj = CreateProductObject(ObjectType::kPlus, 10); + ASSERT_EQ(obj->DoIt(1, 2), 13); + obj = CreateProductObject(ObjectType::kMinus, 10); + ASSERT_EQ(obj->DoIt(1, 2), 9); +} +``` + +## `FunctionFactory` + +`FunctionFactory`解决的问题与`ObjectFactory`类似,其主要为了简化抽象工厂的复杂性,对于大多数场景,`FunctionFactory`更容易编写和上手。 + +其应用方式如下示例: + +```c++ +// 定义Key,用于标记工厂将创建的产品类别 +enum class FunctionType { + kPlus, + kMinus +}; + +// 定义接口和工厂,使用FunctionType作为查找关键字,int32_t(int32_t, int32_t)为函数协议 +using TestFunctionFactory = FunctionFactory; + +// 定义产品 +int32_t Plus(int32_t lhs, int32_t rhs) { + return lhs + rhs; +} + +// 定义产品,可能在另外的.cpp文件中 +int32_t Minus(int32_t lhs, int32_t rhs) { + return lhs - rhs; +} + +// 注册产品,注册产品的方式随着Key,接口,工厂,产品之间的分布以及加载可灵活处理,保证注册成功即可 +// 此处利用单件模式来确保注册 +bool RegisterFunction() { + RegisterFactoryFunction(FunctionType::kPlus, Plus); + RegisterFactoryFunction(FunctionType::kMinus, Minus); +} +void AutoFunctionLoader() { + static auto testObjectFactor = RegisterFunction(); +} + +TEST(TestFactory, TestAll) { + // 加载产品 + AutoFunctionLoader(); + + // 获取产品对象,需要判空,本示例略 + auto func = CreateProductFunction(FunctionType::kPlus); + ASSERT_EQ(func(1, 2), 3); + func = CreateProductFunction(FunctionType::kMinus); + ASSERT_EQ(func(1, 2), -1); +} +``` + + +# Utility + +## `ToRef` + +针对方舟新增代码和重构代码中指针传参的场景,期望以引用的方式来替代,即确保所有指针均已经过校验,再以引用的方式传递给被调用的函数,被调用函数多数情况下不应承担函数参数中指针为空的风险以及判断的开销。 + +通常的写法为(示例中`DoIt`和`Run`可当做第三方接口,无法更改): + +```c++ +A *DoIt(B &b); +void Run(B *b) { + CHECK_NULL_FATAL(b); + // ... + A *a = DoIt(*b); + CHECK_NULL_FATAL(a); + a->Do; +} +``` + +期望多数指针获取即引用: + +```c++ +A *DoIt(B &b); +void Run(B *b) { + B &bRef = utils::ToRef(b); + // ... + A &a = utils::ToRef(DoIt(bRef)); + a.Do; +} +``` + +对于`b`仅单次使用,亦可以调整为: + +```c++ +A *DoIt(B &b); +void Run(B *b) { + // ... + A &a = utils::ToRef(DoIt(utils::ToRef(b))); + a.Do; +} +``` + +## `bit_field_v`&`lbit_field_v` + +使用bit位来标记状态开关组合,是一种既节约内存又能高效编码的设计方法。通常,在枚举定义或常量定义时,会有如下写法: + +```c++ +enum BBAttr : uint32 { + kBBAttrIsEntry = 0x02, + kBBAttrIsExit = 0x04, + kBBAttrWontExit = 0x08, + kBBAttrIsTry = 0x10, + kBBAttrIsTryEnd = 0x20, + kBBAttrIsJSCatch = 0x40, + kBBAttrIsJSFinally = 0x80, + kBBAttrIsCatch = 0x0100, + kBBAttrIsJavaFinally = 0x0200, + kBBAttrArtificial = 0x0400, + kBBAttrIsInLoop = 0x0800, + kBBAttrIsInLoopForEA = 0x1000 +}; +``` + +此设计很明显的欲用位来记录某些属性信息,但位信息比较隐晦,难以维护与阅读。 + +所以需要更加清晰的设计: + +```c++ +enum BBAttr : uint32 { + kBBAttrIsEntry = utils::bit_field_v<1>, + kBBAttrIsExit = utils::bit_field_v<2>, + kBBAttrWontExit = utils::bit_field_v<3>, + kBBAttrIsTry = utils::bit_field_v<4>, + kBBAttrIsTryEnd = utils::bit_field_v<5>, + kBBAttrIsJSCatch = utils::bit_field_v<6>, + kBBAttrIsJSFinally = utils::bit_field_v<7>, + kBBAttrIsCatch = utils::bit_field_v<8>, + kBBAttrIsJavaFinally = utils::bit_field_v<9>, + kBBAttrArtificial = utils::bit_field_v<10>, + kBBAttrIsInLoop = utils::bit_field_v<11>, + kBBAttrIsInLoopForEA = utils::bit_field_v<12> +}; +``` + +其中`bit_field_v`:`uint32`,`lbit_field_v`:`uint64`,未来将按需添加`sbit_field_v`:`uint16`以及`bbit_field_v`:`uint8`。 diff --git a/src/bin/jbc2mpl b/src/bin/jbc2mpl index f13448197f30bbe53b1a4f85c3e0fbdadae09984..ad1de560aa774b77faea69a1c7cac256cf8ede22 100755 Binary files a/src/bin/jbc2mpl and b/src/bin/jbc2mpl differ diff --git a/src/bin/maple b/src/bin/maple index ffa9f86ca6c64fecfd52fde40422533941a167ec..ff1c863bdb610bc6720d9f9741f3e730573056f1 100755 Binary files a/src/bin/maple and b/src/bin/maple differ diff --git a/src/deplibs/libmplutil.a b/src/deplibs/libmplutil.a index f58af516af6afcfdcd5942f4ccf55d58f65217c0..2a4d7fc89d891a3cd900a4075f3f49d5ede319ee 100644 Binary files a/src/deplibs/libmplutil.a and b/src/deplibs/libmplutil.a differ diff --git a/src/maple_driver/include/compiler.h b/src/maple_driver/include/compiler.h index 58a598ef6dcc3defcfc8b8801c73ef22c6872a4b..ecc64d5cdac511bb76fa34e313cc9e1fe640231d 100644 --- a/src/maple_driver/include/compiler.h +++ b/src/maple_driver/include/compiler.h @@ -117,6 +117,7 @@ class MapleCombCompiler : public Compiler { private: std::string realRunningExe; std::unordered_set GetFinalOutputs(const MplOptions &mplOptions) const override; + void GetTmpFilesToDelete(const MplOptions &mplOptions, std::vector &tempFiles) const override; MeOption *MakeMeOptions(const MplOptions &options, MemPool &memPool); Options *MakeMpl2MplOptions(const MplOptions &options, MemPool &memPool); }; diff --git a/src/maple_driver/src/maple_comb_compiler.cpp b/src/maple_driver/src/maple_comb_compiler.cpp index d74263fc376b2c5649608a61803733df3a8903be..7ca5eb8254d01af2421d7deff9c9758052c7c3aa 100644 --- a/src/maple_driver/src/maple_comb_compiler.cpp +++ b/src/maple_driver/src/maple_comb_compiler.cpp @@ -35,6 +35,28 @@ std::string MapleCombCompiler::GetInputFileName(const MplOptions &options) const return options.GetOutputFolder() + options.GetOutputName() + ".mpl"; } +void MapleCombCompiler::GetTmpFilesToDelete(const MplOptions &mplOptions, std::vector &tempFiles) const { + std::string filePath; + if ((realRunningExe == kBinNameMe) && !mplOptions.HasSetGenMeMpl()) { + filePath = mplOptions.GetOutputFolder() + mplOptions.GetOutputName() + ".me.mpl"; + } else if (mplOptions.HasSetGenVtableImpl() == false) { + filePath = mplOptions.GetOutputFolder() + mplOptions.GetOutputName() + ".VtableImpl.mpl"; + } + tempFiles.push_back(filePath); + filePath = mplOptions.GetOutputFolder() + mplOptions.GetOutputName() + ".data.muid"; + tempFiles.push_back(filePath); + filePath = mplOptions.GetOutputFolder() + mplOptions.GetOutputName() + ".func.muid"; + tempFiles.push_back(filePath); + for (auto iter = tempFiles.begin(); iter != tempFiles.end();) { + std::ifstream infile; + infile.open(*iter); + if (infile.fail()) { + iter = tempFiles.erase(iter); + } else { + ++iter; + } + } +} std::unordered_set MapleCombCompiler::GetFinalOutputs(const MplOptions &mplOptions) const { std::unordered_set finalOutputs; diff --git a/src/maple_me/include/dse.h b/src/maple_me/include/dse.h index ed87b8bacbb1bb6e3bed1ef15a2ebb442ca65aff..4a7f2eb46f93ccccc5f060a0465a08cb8f0990cf 100644 --- a/src/maple_me/include/dse.h +++ b/src/maple_me/include/dse.h @@ -27,12 +27,13 @@ namespace maple { class DSE { public: DSE(std::vector &&bbVec, BB &commonEntryBB, BB &commonExitBB, SSATab &ssaTab, - Dominance &postDom, bool enableDebug = false) + Dominance &postDom, bool enableDebug = false, bool decouple = false) : enableDebug(enableDebug), bbVec(bbVec), commonEntryBB(commonEntryBB), commonExitBB(commonExitBB), ssaTab(ssaTab), postDom(postDom), bbRequired(bbVec.size(), false), - exprRequired(ssaTab.GetVersionStTableSize(), false) {} + exprRequired(ssaTab.GetVersionStTableSize(), false), + decoupleStatic(decouple) {} ~DSE() = default; @@ -117,6 +118,7 @@ class DSE { std::vector exprRequired; std::forward_list> workList{}; bool cfgUpdated = false; + bool decoupleStatic = false; }; } // namespace maple #endif // MAPLE_ME_INCLUDE_DSE_H diff --git a/src/maple_me/include/hdse.h b/src/maple_me/include/hdse.h index 083d39571fb628399a4324dd8c7ff1ca55382e83..7509112fdd058f6d363625075ba332de2ac91e79 100644 --- a/src/maple_me/include/hdse.h +++ b/src/maple_me/include/hdse.h @@ -23,7 +23,7 @@ class MeIRMap; class HDSE { public: HDSE(MIRModule &mod, const MapleVector &bbVec, BB &commonEntryBB, BB &commonExitBB, SSATab &ssaTab, - Dominance &pDom, IRMap &map, bool enabledDebug = false) + Dominance &pDom, IRMap &map, bool enabledDebug = false, bool decouple = false) : hdseDebug(enabledDebug), mirModule(mod), bbVec(bbVec), @@ -32,7 +32,8 @@ class HDSE { ssaTab(ssaTab), postDom(pDom), irMap(map), - bbRequired(bbVec.size(), false) {} + bbRequired(bbVec.size(), false), + decoupleStatic(decouple) {} virtual ~HDSE() = default; @@ -99,6 +100,7 @@ class HDSE { std::vector bbRequired; std::vector exprLive; std::forward_list workList; + bool decoupleStatic = false; }; } // namespace maple #endif // MAPLE_ME_INCLUDE_HDSE_H diff --git a/src/maple_me/include/me_dse.h b/src/maple_me/include/me_dse.h index 6efd7ed45d30cb3ac09e29aa8ddebd94a5a02102..16b5fa47cef74e8d6ce88c7f01ec86ac42daaf69 100644 --- a/src/maple_me/include/me_dse.h +++ b/src/maple_me/include/me_dse.h @@ -29,7 +29,7 @@ class MeDSE : public DSE { MeDSE(MeFunction *f, Dominance *dom, bool enabledDebug) : DSE(std::vector(f->GetAllBBs().begin(), f->GetAllBBs().end()), *f->GetCommonEntryBB(), *f->GetCommonExitBB(), *f->GetMeSSATab(), - *dom, enabledDebug), + *dom, enabledDebug, MeOption::decoupleStatic), func(*f) {} virtual ~MeDSE() = default; diff --git a/src/maple_me/include/me_hdse.h b/src/maple_me/include/me_hdse.h index 2552246cb2a952c63e113f94478abaad3f07632f..0111484c64ac3062c00c7de906db7b30e8ffc07f 100644 --- a/src/maple_me/include/me_hdse.h +++ b/src/maple_me/include/me_hdse.h @@ -26,7 +26,7 @@ class MeHDSE : public HDSE { public: MeHDSE(MeFunction &f, Dominance &pDom, IRMap &map, bool enabledDebug) : HDSE(f.GetMIRModule(), f.GetAllBBs(), *f.GetCommonEntryBB(), *f.GetCommonExitBB(), *f.GetMeSSATab(), - pDom, map, enabledDebug) {} + pDom, map, enabledDebug, MeOption::decoupleStatic) {} virtual ~MeHDSE() = default; void RunHDSE(); diff --git a/src/maple_me/include/me_option.h b/src/maple_me/include/me_option.h index 968631706eb231bcb0263d91cabbe6882dd35d4a..46d078fd20c0d995995393457c9582b087a242e5 100644 --- a/src/maple_me/include/me_option.h +++ b/src/maple_me/include/me_option.h @@ -93,6 +93,7 @@ class MeOption { static bool lpreSpeculate; static bool spillAtCatch; static bool optDirectCall; + static bool decoupleStatic; private: std::unordered_set skipPhases; MapleAllocator optionAlloc; diff --git a/src/maple_me/src/dse.cpp b/src/maple_me/src/dse.cpp index 59d5dc714d912663d011d5ef4b3ef577600467fd..27d18131482a967efaee29013e437227d928aaa2 100644 --- a/src/maple_me/src/dse.cpp +++ b/src/maple_me/src/dse.cpp @@ -42,7 +42,7 @@ bool DSE::ExprNonDeletable(const BaseNode &expr) { case OP_dread: { auto &dread = static_cast(expr); const MIRSymbol &sym = dread.GetMIRSymbol(); - return sym.IsVolatile() || sym.IsTypeVolatile(dread.GetFieldID()); + return sym.IsVolatile() || sym.IsTypeVolatile(dread.GetFieldID()) || (decoupleStatic && sym.IsGlobal()); } case OP_iread: { auto &iread = static_cast(expr); diff --git a/src/maple_me/src/hdse.cpp b/src/maple_me/src/hdse.cpp index 7df4fa3b96782c6d8fc2b95dde3fe311dc2771f6..56c83234cd6f49bc074d1db9a19191a7dbb2e781 100644 --- a/src/maple_me/src/hdse.cpp +++ b/src/maple_me/src/hdse.cpp @@ -189,7 +189,8 @@ bool HDSE::ExprNonDeletable(MeExpr &meExpr) { } case kMeOpVar: { auto &varMeExpr = static_cast(meExpr); - return varMeExpr.IsVolatile(ssaTab); + return varMeExpr.IsVolatile(ssaTab) || + (decoupleStatic && ssaTab.GetSymbolOriginalStFromID(varMeExpr.GetOStIdx())->GetMIRSymbol()->IsGlobal()); } case kMeOpIvar: { auto &opIvar = static_cast(meExpr); diff --git a/src/maple_util/include/literal_str_name.h b/src/maple_util/include/literal_str_name.h index 6705d693c0df1769c569e5c2c3e04c5a8d67a5fa..a2227e4bd44b2e6f0413019853256fb25f53a809 100644 --- a/src/maple_util/include/literal_str_name.h +++ b/src/maple_util/include/literal_str_name.h @@ -19,7 +19,6 @@ #include "muid.h" // literal string naming is shared between maple compiler and runtime, thus not in namespace maplert - #ifdef MUID_LENGTH #undef MUID_LENGTH #define MUID_LENGTH 16 @@ -47,12 +46,12 @@ class LiteralStrName { const char *dataStart = reinterpret_cast(data); const char *end = dataStart + len; while (dataStart < end) { - hash = (hash << 5) - hash + *dataStart++; + hash = (hash << 5) - hash + *dataStart++; // calculate the hash code of data } } else { const char16_t *end = data + len; while (data < end) { - hash = (static_cast(hash) << 5) - hash + *data++; + hash = (static_cast(hash) << 5) - hash + *data++; // calculate the hash code of data } } return hash; diff --git a/src/maple_util/include/muid.h b/src/maple_util/include/muid.h index beb29254addb82ce86c5aa5aec64a88308f0e0f8..f598032f5885154281a440977c882cf9c1ad07b4 100644 --- a/src/maple_util/include/muid.h +++ b/src/maple_util/include/muid.h @@ -95,11 +95,13 @@ struct MUID { std::string ToStr() const { std::stringstream sbuf; #ifdef USE_64BIT_MUID - sbuf << std::setfill('0') << std::setw(8) << std::hex << data.words[1] << - std::setfill('0') << std::setw(8) << std::hex << data.words[0]; + // 8 spaces to 64 bit + sbuf << std::setfill('0') << std::setw(8) << std::hex << data.words[1] << std::setfill('0') << std::setw(8) << + std::hex << data.words[0]; #else - sbuf << std::setfill('0') << std::setw(16) << std::hex << data.words[1] << - std::setfill('0') << std::setw(16) << std::hex << data.words[0]; + // 16 spaces to 32 bit + sbuf << std::setfill('0') << std::setw(16) << std::hex << data.words[1] << std::setfill('0') << std::setw(16) << + std::hex << data.words[0]; #endif // USE_64BIT_MUID return sbuf.str(); }