-
Notifications
You must be signed in to change notification settings - Fork 18
Coding Standards
Допускается длина строки не более 80 символов. Исключение делается только для строковых констант, не умещающихся в данный предел. Использование символа табуляции в коде не допускается, данные символы должны быть заменены на пробелы (автоматическая замена поддерживается в большинстве средств разработки). Стандартный размер отступа - 2 символа, все завершающие пробельные символы должны удаляться.
Все комментарии, размещенные в коде, должны быть написаны на английском языке. Допускается два вида комментариев:
// Обычный комментарий
/// Документирующий комментарий оформляется в формате
/// Doxygen-комментария (http://www.doxygen.org/).
///
/// Документирующие Doxygen-комментарии должны быть написаны для всех открытых
/// методов, членов, типов класса, всех доступных извне элементов (в том числе
/// классов, пространств имен, типов).
/// \note Рекомендуется указывать комментарии и для остальных объектов.
В начале каждого файла должен содержаться блок комментариев кратко описывающий содержимое файла, а также информация о лицензионном соглашении (NOTICE).
//===--- PrivateAnalysis.h - Private Variable Analyzer ----------*- C++ -*-===//
//
// Traits Static Analyzer (SAPFOR)
//
//
// Copyright 2018 DVM System Group
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//===----------------------------------------------------------------------===//
//
// This file defines passes to determine locations which can be privatized.
// We use data-flow framework to implement this kind of analysis. This file
// contains elements which is necessary to determine this framework.
// The following articles can be helpful to understand it:
// * "Automatic Array Privatization" Peng Tu and David Padua
// * "Array Privatization for Parallel Execution of Loops" Zhiyuan Li.
//
//===----------------------------------------------------------------------===//
Перед объектами, расположенными в пространстве имен отступы не ставятся:
namespace tsar {
/// Color of a node which depth-first search (DFS) has visited.
enum DFSColor {
...
После заголовка функции, ключевого слова template
, специализируемого класса или функции отступы не ставятся.
/// Return true if the specified directed graph is acyclic.
///
/// \param [in] G Directed graph, it can not be null.
/// \pre The llvm::GraphTraits class should be specialized by GraphType.
/// Note that GraphType is generally a pointer type, for example BasicBlock *.
template<class GraphType> inline bool isDAG(GraphType G) {
return !findBackEdge<GraphType>(G).first;
}
Отступы ставятся после ключевых слов if
, for
, while
и аналогичных.
Если параметры функции не умещаются в границы 80 символов, то перед ними ставится отступ 4 символа.
template<class location_iterator, class ResultSet>
static void difference(
const location_iterator &LocBegin, const location_iterator &LocEnd,
const LocationSet &LocSet, ResultSet &Result) {
if (LocSet.mLocations.empty())
...
Открывающая фигурная скобка размещается на той же строке что и предшествующая ей конструкция. Ключевое слово else
размещается на той же строке, что и предшествующая закрывающая фигурная скобка. Если в блоке if-then-else
хотя бы один раз встречаются фигурные скобки, то фигурные скобки должны стоят во всех ветках. *Строки идут подряд без пропусков за исключением отступов перед введением новых классов, типов, функций, блока конструкций using
и #include
.
if (SToNode == Blocks.end()) {
BBToN.second->addSuccessor(ExitNode);
ExitNode->addPredecessor(BBToN.second);
} else if (*SI == LT::getHeader(L)) {
...
} else if (SToNode->second != BBToN.second) {
BBToN.second->addSuccessor(SToNode->second);
}
Подключаемые заголовочные файлы должны быть перечислены в лексикографическом порядке. При этом сначала указываются файлы относящиеся непосредственно к изменяемому проекту, данные файлы экранируются "
. Имена остальных файлов экранируются <...>
, сначала указываются файлы относящиеся к BCL, затем к Clang, LLVM и самыми последними указываются файлы стандартной библиотеки язка С++.
Если заголовочные файлы доволяются в файл .cpp
то независимо от имени указывается заголовончый файл логически связанный с данным .cpp
файлом (чаще всего это файл с тем же именем, но расширением .h
).
Пример заголовочных файлов, подключаемых в файле tsar/lib/Support/Clang/Utils.cpp
:
#include "tsar/Support/Clang/Utils.h"
#include "tsar/Support/Utils.h"
#include <bcl/utility.h>
#include <clang/Analysis/CFG.h>
#include <clang/ASTMatchers/ASTMatchFinder.h>
#include <clang/Basic/LangOptions.h>
#include <clang/Format/Format.h>
#include <clang/Frontend/ASTUnit.h>
#include <clang/Lex/Lexer.h>
#include <clang/Tooling/Tooling.h>
#include <llvm/ADT/DenseSet.h>
#include <llvm/ADT/SmallPtrSet.h>
#include <llvm/Support/Debug.h>
#include <llvm/Support/raw_ostream.h>
#include <numeric>
#include <regex>
Для защиты от повторного подключения файлов внутри заголовочных файлов используются макросы (вместо директивы #pragma once
):
#ifndef TSAR_CLANG_UTILS_H
#define TSAR_CLANG_UTILS_H
...
#endif//TSAR_CLANG_UTILS_H
Имена классов и типов должны начинаться с заглавной буквы. Имена функций начинаются со строчной буквы. Использование нижнего подчеркивания _
и строчных букв в именах классов и типов допускается только для stl-подобных конструкций.
...
/// This type used to iterate over all nodes in the region body.
using node_iterator = std::vector<DFNode *>::const_iterator;
/// This type used to iterate over internal regions.
using region_iterator = std::vector<DFRegion *>::const_iterator;
...
/// Return iterator that points to the beginning of the nodes list.
node_iterator node_begin() const { return mNodes.begin(); }
/// Return iterator that points to the ending of the nodes list.
node_iterator node_end() const { return mNodes.end(); }
...
Имена переменных должны начинаться с заглавной буквы. Для закрытых членов класса должен ставиться префикс m
, для глобальных переменных (использование которых не рекомедуется) - префикс g
.
Имена должны быть понятны и точно описывать назначение именуемой сущности. Допускаются общеупотребительные в рамках проекта сокращения имен (BasicBlock - BB, Function - F, Control Flow Graph - CFG и т.д.), или сокращения имен с небольшой областью видимости для более компактного размещения кода.
...
for (auto I = LT::block_begin(L), E = LT::block_end(L); I != E; ++I) {
if (Blocks.count(*I))
continue;
auto *N = new DFBlock(*I);
R->addNode(N);
Blocks.insert(std::make_pair(*I, N));
}
...
Закрытые методы и члены классов должны быть размещены в конце описания класса.
Объявление класса должно начинаться с секции public
, затем следует секция protected
и секция private
.
...
/// Specifies that there are unknown instructions in the node.
///
/// \return False if it has been already specified.
bool addUnknownInst(llvm::Instruction *I) {
assert(I && "Instruction must not be null!");
return mUnknownInsts.insert(I).second;
}
private:
LocationSet mDefs;
LocationSet mMayDefs;
LocationSet mUses;
llvm::AliasSetTracker mExplicitAccesses;
PointerSet mAddressAccesses;
InstructionSet mUnknownInsts;
};
Использование открытых членов класса допускается только в структурах, если отсутствуют другие закрытые члены класса и только если структура представляет из себя простой контейнер для хранения данных, не реализующий дополнительной функциональности.
struct GlobalOptions {
/// Print only names of files instead of full paths.
bool PrintFilenameOnly = false;
/// Disallow unsafe integer type cast in analysis passes.
bool IsSafeTypeCast = true;
/// Assume that subscript expression is in bounds value of an array dimension.
bool InBoundsSubscripts = false;
...
};
Классы не допускающие копирования должны закрыто наследовать класс bcl::Uncopyable
определенный в файле bcl/utility.h
namespace llvm {
/// This pass determines locations which can be privatized.
class PrivateRecognitionPass :
public FunctionPass, private bcl::Uncopyable {
...
Для функций должен соблюдаться следующий порядок задания параметров: сначала указываются входные, затем выходные. Не следует добавлять новые параметры функции в конец ее объявления только потому, что они были добавлены последними.
Предпочитайте писать компактные функции. Если функция занимает слишком много строк кода, стоит подумать о том как ее разбить на несколько компактных. Это поможет сделать код более понятным, но не должно нарушать логическую структуру программы. Сопровождать и отлаживать большие функции непросто, особенно когда этим занимаются другие разработчики.
Перечисления, используемые для задания вида объекта, должны начинаться со вспомогательного элемента с префиксом FIRST
и заканчиваться тремя вспомогательными элементами с префиксами LAST
, INVALID
, NUMBER
соответственно.
/// Kind of a node.
/// If you add a new kind of region it should be in the range between
/// FIRST_KIND_REGION and LAST_KIND_REGION
enum Kind {
FIRST_KIND = 0,
KIND_BLOCK = FIRST_KIND,
KIND_ENTRY,
KIND_EXIT,
KIND_LATCH,
FIRST_KIND_REGION,
KIND_LOOP = FIRST_KIND_REGION,
KIND_FUNCTION,
LAST_KIND_REGION = KIND_FUNCTION,
LAST_KIND = LAST_KIND_REGION,
INVALID_KIND,
NUMBER_KIND = INVALID_KIND,
};
В некоторых ситуация допускается использование перечислений без вспомогательных элементов. В этом случае к именам элементов применяются правила именования переменных. Например, следующее перечисление задает битовые флаги. Для удобства используется перегрузка для битовых операций над элементами перечисления, реализованная в LLVM и позволяющая избежать явных приведений типов (LoopBoundKind BoundFlag = LoopStartIsKnown | LoopEndIsKnown
).
#include <llvm/ADT/BitmaskEnum.h>
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE()
enum LoopBoundKind : short {
LoopBoundIsUnknown = 0,
LoopStartIsKnown = 1u << 0,
LoopEndIsKnown = 1u << 1,
LoopStepIsKnown = 1u << 2,
LoopBoundUnsigned = 1u << 3,
LLVM_MARK_AS_BITMASK_ENUM(LoopBoundUnsigned)
};
Для сокращения количества ошибок в коде и времени затраченного на их поиск рекомендуется использовать конструкции assert
. Для точного описания проверяемого утверждения следует использовать строковую константу:
assert(*I == GT::getEntryNode(DFG) &&
"The first node in the topological order differs from the entry node in the data-flow framework!");
Чтобы пометить область код, которая никода не должны выполняться можно использовать конструкцию LLVM (llvm_unreachable
):
switch (getKind()) {
default:
llvm_unreachable("Unknown kind of an alias node!");
break;
case KIND_TOP:
...
В данном случае аварийный останов программы произойдет, если условие *I == GT::getEntryNode(DFG)
окажется ложно. При этом текстовое сообщение, описывающее проверяемое утверждение, не влияет на выполнение проверки и используется в пояснительных целях.
Конструкции assert
и llvm_unreachabel
могут быть удалены на этапе компиляции и не должны менять поведение программы. После их отключения программа должна оставаться корректной. Следовательно, внутри данных конструкций нельзя изменять переменные программы, используемые не только с целью отладки, вызывать функции, изменяющие состояние программы.
Для проверки утверждений на этапе компиляции могут использоваться конструкции static_assert
:
static_assert(!IsCellExist<CellNext, CellKey>::value,
"Each cell must be presented only once in the map!");
Написать нам можно через форму связи на сайте проекта DVM-системы.