Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
S
systemc-clang
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
2
Issues
2
List
Boards
Labels
Service Desk
Milestones
Operations
Operations
Incidents
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
caesr-pub
systemc-clang
Commits
ac1d4b98
Commit
ac1d4b98
authored
Dec 31, 2018
by
Hiren Patel
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'code-cleanup' into 'master'
Code cleanup See merge request !11
parents
52d34b6b
910e0a5d
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
160 additions
and
129 deletions
+160
-129
CMakeLists.txt
CMakeLists.txt
+4
-0
paths.sh
paths.sh
+3
-3
scripts/paths.fish
scripts/paths.fish
+35
-0
src/CMakeLists.txt
src/CMakeLists.txt
+36
-36
src/FindTemplateTypes.h
src/FindTemplateTypes.h
+9
-13
src/FindWait.h
src/FindWait.h
+1
-1
src/InterfaceDecl.cpp
src/InterfaceDecl.cpp
+1
-1
src/PortDecl.cpp
src/PortDecl.cpp
+19
-18
src/PortDecl.h
src/PortDecl.h
+2
-2
src/Signal.cpp
src/Signal.cpp
+1
-1
src/SuspensionAutomata.h
src/SuspensionAutomata.h
+1
-2
src/SystemCClang.cpp
src/SystemCClang.cpp
+48
-52
No files found.
CMakeLists.txt
View file @
ac1d4b98
cmake_minimum_required
(
VERSION 2.6
)
project
(
systemc-clang
)
# Use C++ standard version to c++17
set
(
CMAKE_CXX_STANDARD 17
)
# Always keep it on
set
(
CMAKE_CXX_STANDARD_REQUIRED ON
)
option
(
USE_SAUTO
"Use suspension-automata library."
OFF
...
...
paths.sh
View file @
ac1d4b98
...
...
@@ -4,7 +4,6 @@
## It is important to use this script to set all the necessary paths.
##
# Path for where the binaries are for clang
# SET this.
export
LLVM_BUILD_DIR
=
/home/
$USER
/bin/clang-7.0.0/
...
...
@@ -26,10 +25,11 @@ LLVMCONFIG=$LLVM_BUILD_DIR/bin/llvm-config
# Alternatively, you can use gcc and g++, but some flags don't work.
export
CC
=
clang
export
CXX
=
clang++
export
LLVM_CXX_FLAGS
=
`
$LLVMCONFIG
--cxxflags
`
export
LLVM_CXX_FLAGS
=
"
`
$LLVMCONFIG
--cxxflags
`
-fno-aligned-allocation"
# Generate all the flags.
export
LLVM_CXX_FLAGS
=
"
$LLVM_CXX_FLAGS
-fvisibility-inlines-hidden"
export
LLVM_LIBS
=
`
$LLVMCONFIG
--libs
`
export
LLVM_LIBS
=
`
$LLVMCONFIG
--libs
`
export
LLVM_LD_FLAGS
=
`
$LLVMCONFIG
--ldflags
`
export
LLVM_LD_FLAGS
=
`
echo
$LLVM_LD_FLAGS
|
sed
's/ *$//g'
`
scripts/paths.fish
0 → 100644
View file @
ac1d4b98
#! /usr/bin/fish
###===================================================================
##
## It is important to use this script to set all the necessary paths.
## This is a fish shell script.
##===================================================================
# Path for where the binaries are for clang
# SET this.
set -x LLVM_BUILD_DIR /home/$USER/bin/clang-7.0.0/
# Path where SystemC is installed
# SET this.
set -x SYSTEMC /home/$USER/code/systemc-2.3.3/systemc/
# Path for the systemc-clang build directory
# SET this.
set -x SYSTEMC_CLANG_BUILD_DIR /home/$USER/code/systemc-clang-build/
##===================================================================
set -x LLVMCOMPONENT cppbackend
set -x RTTIFLAG -fno-rtti
set -x LLVMCONFIG $LLVM_BUILD_DIR/bin/llvm-config
# New llvm/clang uses flags that are different than GNU gcc's
# Alternatively, you can use gcc and g++, but some flags don't work.
set -x CC clang
set -x CXX clang++
set -x LLVM_CXX_FLAGS (eval $LLVMCONFIG --cxxflags)
# Generate all the flags.
# -fno-aligned-allocation needed for c++17
set -x LLVM_CXX_FLAGS "$LLVM_CXX_FLAGS -fvisibility-inlines-hidden -fno-aligned-allocation"
set -x LLVM_LIBS (eval $LLVMCONFIG --libs)
set -x LLVM_LD_FLAGS (eval $LLVMCONFIG --ldflags)
set -x LLVM_LD_FLAGS (echo $LLVM_LD_FLAGS | sed 's/ *$//g')
src/CMakeLists.txt
View file @
ac1d4b98
add_library
(
libsystemc-clang
ModuleDecl.cpp
FindModule.cpp
SCModules.cpp
FindSCMain.cpp
FindPorts.cpp
FindTLMInterfaces.cpp
FindEvents.cpp
FindGlobalEvents.cpp
FindEntryFunctions.cpp
FindSensitivity.cpp
FindWait.cpp
FindNotify.cpp
FindSignals.cpp
FindSimTime.cpp
FindConstructor.cpp
SystemCClang.cpp
FindNetlist.cpp
FindArgument.cpp
Automata.cpp
SuspensionAutomata.cpp
# SCuitable/GlobalSuspensionAutomata.cpp
# SCuitable/FindGPUMacro.cpp
ModuleDecl.cpp
FindModule.cpp
SCModules.cpp
FindSCMain.cpp
FindPorts.cpp
FindTLMInterfaces.cpp
FindEvents.cpp
FindGlobalEvents.cpp
FindEntryFunctions.cpp
FindSensitivity.cpp
FindWait.cpp
FindNotify.cpp
FindSignals.cpp
FindSimTime.cpp
FindConstructor.cpp
SystemCClang.cpp
FindNetlist.cpp
FindArgument.cpp
Automata.cpp
SuspensionAutomata.cpp
SCuitable/GlobalSuspensionAutomata.cpp
SCuitable/FindGPUMacro.cpp
####################################
#From here the files are for the reflection database
####################################
#Utility.cpp
EntryFunctionContainer.cpp
WaitContainer.cpp
NotifyContainer.cpp
EventDecl.cpp
ProcessDecl.cpp
Signal.cpp
PortDecl.cpp
InterfaceDecl.cpp
Model.cpp
WaitCalls.cpp
NotifyCalls.cpp
EventContainer.cpp
#Utility.cpp
EntryFunctionContainer.cpp
WaitContainer.cpp
NotifyContainer.cpp
EventDecl.cpp
ProcessDecl.cpp
Signal.cpp
PortDecl.cpp
InterfaceDecl.cpp
Model.cpp
WaitCalls.cpp
NotifyCalls.cpp
EventContainer.cpp
# AST MATCHERS
# matchers/sc_module.cpp
)
)
src/FindTemplateTypes.h
View file @
ac1d4b98
...
...
@@ -96,28 +96,24 @@ namespace scpar {
return
template_types_
;
}
void
printTemplateArguments
(
llvm
::
raw_ostream
&
os
,
int
tabn
=
0
)
{
vector
<
string
>
template_arguments
;
//{ getTemplateArguments() };
// type_vector_t::iterator
// for (auto mit = template_types_.begin(); mit != template_types_.end(); mit++) {
void
printTemplateArguments
(
llvm
::
raw_ostream
&
os
)
{
vector
<
string
>
template_arguments
;
for
(
auto
const
&
mit
:
template_types_
)
{
for
(
auto
i
{
0
};
i
<
tabn
;
++
i
)
{
os
<<
" "
;
}
os
<<
"- "
<<
mit
.
first
<<
", type ptr: "
<<
mit
.
second
;
os
<<
"
\n
"
;
//os << "\n port type: " << mit.first << " ";
//<< ", type ptr: " << mit.second;
//os << "\n";
template_arguments
.
push_back
(
mit
.
first
);
}
// Print the template arguments to the output stream
os
<<
"
=
"
;
os
<<
"
, "
<<
template_arguments
.
size
()
<<
" arguments,
"
;
for
(
auto
const
&
targ
:
template_arguments
)
{
os
<<
targ
<<
"
"
;
os
<<
targ
<<
" "
;
}
}
vector
<
string
>
getTemplateArguments
()
{
vector
<
string
>
template_arguments
;
vector
<
string
>
getTemplateArguments
()
{
vector
<
string
>
template_arguments
;
// type_vector_t::iterator
// for ( auto mit = template_types_.begin(); mit != template_types_.end(); ++mit ) {
for
(
auto
const
&
mit
:
template_types_
)
{
...
...
src/FindWait.h
View file @
ac1d4b98
...
...
@@ -13,7 +13,7 @@ namespace scpar {
class
FindWait
:
public
RecursiveASTVisitor
<
FindWait
>
{
public:
typedef
vector
<
CallExpr
*
>
waitListType
;
typedef
vector
<
CallExpr
*
>
waitListType
;
typedef
pair
<
CXXMethodDecl
*
,
vector
<
string
>
>
processWaitEventPairType
;
typedef
map
<
CXXMethodDecl
*
,
vector
<
string
>
>
processWaitEventMapType
;
...
...
src/InterfaceDecl.cpp
View file @
ac1d4b98
...
...
@@ -44,5 +44,5 @@ void InterfaceDecl::dump(raw_ostream & os, int tabn) {
}
os
<<
"InterfaceDecl "
<<
this
<<
" '"
<<
_name
<<
"' FindTemplateTypes "
<<
_templateType
;
_templateType
->
printTemplateArguments
(
os
,
1
);
_templateType
->
printTemplateArguments
(
os
);
}
src/PortDecl.cpp
View file @
ac1d4b98
...
...
@@ -5,41 +5,42 @@ using namespace scpar;
using
namespace
std
;
PortDecl
::~
PortDecl
()
{
if
(
_templateType
!=
nullptr
)
{
delete
_templateType
;
if
(
template_type_
!=
nullptr
)
{
delete
template_type_
;
}
}
PortDecl
::
PortDecl
()
:
_name
{
"NONE"
},
_templateType
{
nullptr
}
{
PortDecl
::
PortDecl
()
:
port_name_
{
"NONE"
},
template_type_
{
nullptr
}
{
}
PortDecl
::
PortDecl
(
const
string
&
name
,
FindTemplateTypes
*
tt
)
:
_name
{
name
},
_templateType
{
tt
}
{
PortDecl
::
PortDecl
(
const
string
&
name
,
FindTemplateTypes
*
tt
)
:
port_name_
{
name
},
template_type_
{
tt
}
{
}
PortDecl
::
PortDecl
(
const
PortDecl
&
from
)
{
_name
=
from
.
_name
;
PortDecl
::
PortDecl
(
const
PortDecl
&
from
)
{
port_name_
=
from
.
port_name_
;
// This is necessary to allow FindPorts to go out of scope.
_templateType
=
new
FindTemplateTypes
(
*
from
.
_templateType
)
;
template_type_
=
new
FindTemplateTypes
{
*
from
.
template_type_
}
;
}
void
PortDecl
::
setModuleName
(
const
string
&
name
)
{
_name
=
name
;
void
PortDecl
::
setModuleName
(
const
string
&
name
)
{
port_name_
=
name
;
}
string
PortDecl
::
getName
()
const
{
return
_name
;
return
port_name_
;
}
FindTemplateTypes
*
PortDecl
::
getTemplateType
()
{
return
_templateType
;
return
template_type_
;
}
void
PortDecl
::
dump
(
raw_ostream
&
os
,
int
tabn
)
{
for
(
auto
i
=
0
;
i
<
tabn
;
++
i
)
{
os
<<
" "
;
}
os
<<
"PortDecl "
<<
this
<<
" '"
<<
_name
<<
"' FindTemplateTypes "
<<
_templateType
;
_templateType
->
printTemplateArguments
(
os
,
1
);
void
PortDecl
::
dump
(
llvm
::
raw_ostream
&
os
,
int
tabn
)
{
//os << "PortDecl " << this << " '" << port_name_ << "' FindTemplateTypes " << template_type_;
os
<<
"Port name: "
<<
port_name_
<<
" "
;
template_type_
->
printTemplateArguments
(
os
);
}
src/PortDecl.h
View file @
ac1d4b98
...
...
@@ -30,8 +30,8 @@ namespace scpar {
void
dump
(
raw_ostream
&
,
int
tabn
=
0
);
private:
string
_name
;
FindTemplateTypes
*
_templateType
;
string
port_name_
;
FindTemplateTypes
*
template_type_
;
};
}
#endif
src/Signal.cpp
View file @
ac1d4b98
...
...
@@ -66,5 +66,5 @@ void
}
os
<<
"Signal "
<<
this
<<
" '"
<<
_name
<<
"' FindTemplateTypes "
<<
_sig
->
getTemplateTypes
()
<<
"' FieldDecl' "
<<
_sig
->
getASTNode
();
_sig
->
getTemplateTypes
()
->
printTemplateArguments
(
os
,
tabn
);
_sig
->
getTemplateTypes
()
->
printTemplateArguments
(
os
);
}
src/SuspensionAutomata.h
View file @
ac1d4b98
...
...
@@ -130,8 +130,7 @@ namespace scpar {
typedef
pair
<
State
*
,
vector
<
SusCFG
*>
>
stateCommonCodeBlockPairType
;
typedef
map
<
State
*
,
vector
<
SusCFG
*>
>
stateCommonCodeBlockMapType
;
SuspensionAutomata
(
vector
<
WaitContainer
*>
,
CXXMethodDecl
*
,
ASTContext
*
,
raw_ostream
&
);
SuspensionAutomata
(
vector
<
WaitContainer
*>
,
CXXMethodDecl
*
,
ASTContext
*
,
llvm
::
raw_ostream
&
);
~
SuspensionAutomata
();
void
addRemainingBlocks
(
State
*
,
vector
<
SusCFG
*>&
);
void
checkInsert
(
vector
<
SusCFG
*>
,
vector
<
SusCFG
*>&
);
...
...
src/SystemCClang.cpp
View file @
ac1d4b98
...
...
@@ -17,39 +17,37 @@ bool SystemCConsumer::postFire() {
bool
SystemCConsumer
::
fire
()
{
TranslationUnitDecl
*
tu
=
_context
.
getTranslationUnitDecl
()
;
TranslationUnitDecl
*
tu
{
_context
.
getTranslationUnitDecl
()
}
;
// Reflection database.
_systemcModel
=
new
Model
()
;
_systemcModel
=
new
Model
{}
;
// Find the sc_modules
SCModules
scmod
(
tu
,
_os
)
;
SCModules
scmod
{
tu
,
_os
}
;
// ANI : Do we need FindGlobalEvents?
FindGlobalEvents
fglobals
(
tu
,
_os
)
;
FindGlobalEvents
::
globalEventMapType
eventMap
=
fglobals
.
getEventMap
()
;
FindGlobalEvents
fglobals
{
tu
,
_os
}
;
FindGlobalEvents
::
globalEventMapType
eventMap
{
fglobals
.
getEventMap
()
}
;
_systemcModel
->
addGlobalEvents
(
eventMap
);
SCModules
::
moduleMapType
scmodules
=
scmod
.
getSystemCModulesMap
()
;
SCModules
::
moduleMapType
scmodules
{
scmod
.
getSystemCModulesMap
()
}
;
for
(
SCModules
::
moduleMapType
::
iterator
mit
=
scmodules
.
begin
(),
mitend
=
scmodules
.
end
();
mit
!=
mitend
;
++
mit
)
{
ModuleDecl
*
md
=
new
ModuleDecl
(
mit
->
first
,
mit
->
second
)
;
ModuleDecl
*
md
=
new
ModuleDecl
{
mit
->
first
,
mit
->
second
}
;
_systemcModel
->
addModuleDecl
(
md
);
}
////////////////////////////////////////////////////////////////
// Find the sc_main
////////////////////////////////////////////////////////////////
FindSCMain
scmain
(
tu
,
_os
)
;
FindSCMain
scmain
{
tu
,
_os
}
;
if
(
scmain
.
isSCMainFound
()
)
{
FunctionDecl
*
fnDecl
=
scmain
.
getSCMainFunctionDecl
()
;
FunctionDecl
*
fnDecl
{
scmain
.
getSCMainFunctionDecl
()
}
;
FindSimTime
scstart
(
fnDecl
,
_os
)
;
_systemcModel
->
addSimulationTime
(
scstart
.
returnSimTime
()
);
FindSimTime
scstart
{
fnDecl
,
_os
}
;
_systemcModel
->
addSimulationTime
(
scstart
.
returnSimTime
()
);
}
else
{
_os
<<
"
\n
Could not find SCMain"
;
}
...
...
@@ -57,81 +55,79 @@ bool SystemCConsumer::fire() {
////////////////////////////////////////////////////////////////
// Find the netlist.
////////////////////////////////////////////////////////////////
FindNetlist
findNetlist
(
scmain
.
getSCMainFunctionDecl
())
;
FindNetlist
findNetlist
{
scmain
.
getSCMainFunctionDecl
()
}
;
findNetlist
.
dump
();
_systemcModel
->
addNetlist
(
findNetlist
);
////////////////////////////////////////////////////////////////
// Figure out the module map.
////////////////////////////////////////////////////////////////
Model
::
moduleMapType
moduleMap
=
_systemcModel
->
getModuleDecl
()
;
Model
::
moduleMapType
moduleMap
{
_systemcModel
->
getModuleDecl
()
}
;
for
(
Model
::
moduleMapType
::
iterator
mit
=
moduleMap
.
begin
(),
mitend
=
moduleMap
.
end
();
mit
!=
mitend
;
mit
++
)
{
ModuleDecl
*
mainmd
=
mit
->
second
;
int
numInstances
=
mainmd
->
getNumInstances
()
;
ModuleDecl
*
mainmd
{
mit
->
second
}
;
int
numInstances
{
mainmd
->
getNumInstances
()
}
;
vector
<
ModuleDecl
*>
moduleDeclVec
;
_os
<<
"
\n
"
;
_os
<<
"For module: "
<<
mit
->
first
<<
" num instance : "
<<
numInstances
;
for
(
unsigned
int
num
{
0
};
num
<
numInstances
;
++
num
)
{
ModuleDecl
*
md
=
new
ModuleDecl
()
;
vector
<
EntryFunctionContainer
*
>
_entryFunctionContainerVector
;
FindConstructor
constructor
(
mainmd
->
getModuleClassDecl
(),
_os
)
;
for
(
unsigned
int
num
{
0
};
num
<
numInstances
;
++
num
)
{
ModuleDecl
*
md
=
new
ModuleDecl
{}
;
vector
<
EntryFunctionContainer
*>
_entryFunctionContainerVector
;
FindConstructor
constructor
{
mainmd
->
getModuleClassDecl
(),
_os
}
;
md
->
addConstructor
(
constructor
.
returnConstructorStmt
());
FindPorts
ports
(
mainmd
->
getModuleClassDecl
(),
_os
)
;
md
->
addInputPorts
(
ports
.
getInputPorts
()
);
md
->
addOutputPorts
(
ports
.
getOutputPorts
()
);
md
->
addInputOutputPorts
(
ports
.
getInputOutputPorts
()
);
FindPorts
ports
{
mainmd
->
getModuleClassDecl
(),
_os
}
;
md
->
addInputPorts
(
ports
.
getInputPorts
()
);
md
->
addOutputPorts
(
ports
.
getOutputPorts
()
);
md
->
addInputOutputPorts
(
ports
.
getInputOutputPorts
()
);
FindTLMInterfaces
findTLMInterfaces
(
mainmd
->
getModuleClassDecl
(),
_os
)
;
md
->
addInputInterfaces
(
findTLMInterfaces
.
getInputInterfaces
()
);
md
->
addOutputInterfaces
(
findTLMInterfaces
.
getOutputInterfaces
()
);
md
->
addInputOutputInterfaces
(
findTLMInterfaces
.
getInputOutputInterfaces
()
);
FindTLMInterfaces
findTLMInterfaces
{
mainmd
->
getModuleClassDecl
(),
_os
}
;
md
->
addInputInterfaces
(
findTLMInterfaces
.
getInputInterfaces
()
);
md
->
addOutputInterfaces
(
findTLMInterfaces
.
getOutputInterfaces
()
);
md
->
addInputOutputInterfaces
(
findTLMInterfaces
.
getInputOutputInterfaces
()
);
FindSignals
signals
(
mainmd
->
getModuleClassDecl
(),
_os
)
;
md
->
addSignals
(
signals
.
getSignals
()
);
FindSignals
signals
{
mainmd
->
getModuleClassDecl
(),
_os
}
;
md
->
addSignals
(
signals
.
getSignals
()
);
FindEntryFunctions
findEntries
(
mainmd
->
getModuleClassDecl
(),
_os
)
;
FindEntryFunctions
::
entryFunctionVectorType
*
entryFunctions
=
findEntries
.
getEntryFunctions
()
;
md
->
addProcess
(
entryFunctions
);
FindEntryFunctions
findEntries
{
mainmd
->
getModuleClassDecl
(),
_os
}
;
FindEntryFunctions
::
entryFunctionVectorType
*
entryFunctions
{
findEntries
.
getEntryFunctions
()
}
;
md
->
addProcess
(
entryFunctions
);
for
(
size_t
i
=
0
;
i
<
entryFunctions
->
size
();
i
++
)
{
EntryFunctionContainer
*
ef
=
(
*
entryFunctions
)[
i
]
;
FindSensitivity
findSensitivity
(
constructor
.
returnConstructorStmt
(),
_os
)
;
ef
->
addSensitivityInfo
(
findSensitivity
);
EntryFunctionContainer
*
ef
{
(
*
entryFunctions
)[
i
]
}
;
FindSensitivity
findSensitivity
{
constructor
.
returnConstructorStmt
(),
_os
}
;
ef
->
addSensitivityInfo
(
findSensitivity
);
if
(
ef
->
getEntryMethod
()
==
nullptr
)
{
if
(
ef
->
getEntryMethod
()
==
nullptr
)
{
_os
<<
"ERROR"
;
continue
;
}
FindWait
findWaits
(
ef
->
getEntryMethod
(),
_os
)
;
FindWait
findWaits
{
ef
->
getEntryMethod
(),
_os
}
;
ef
->
addWaits
(
findWaits
);
FindNotify
findNotify
(
ef
->
_entryMethodDecl
,
_os
)
;
FindNotify
findNotify
{
ef
->
_entryMethodDecl
,
_os
}
;
ef
->
addNotifys
(
findNotify
);
/*
/*
SuspensionAutomata suspensionAutomata(findWaits.getWaitCalls(), ef->getEntryMethod(), &_context, llvm::errs());
if (suspensionAutomata.initialize()) {
suspensionAutomata.genSusCFG();
//suspensionAutomata.dumpSusCFG();
suspensionAutomata.genSauto();
suspensionAutomata.genSauto();
//suspensionAutomata.dumpSauto();
ef->addSusCFGAuto(suspensionAutomata);
ef->addSusCFGAuto(suspensionAutomata);
}
*/
*/
_entryFunctionContainerVector
.
push_back
(
ef
);
}
moduleDeclVec
.
push_back
(
md
);
}
_systemcModel
->
addModuleDeclInstances
(
mainmd
,
moduleDeclVec
);
}
/*
FindSCMain scmain(tu, _os);
...
...
@@ -205,11 +201,11 @@ void SystemCConsumer::HandleTranslationUnit(ASTContext & context) {
}
SystemCConsumer
::
SystemCConsumer
(
CompilerInstance
&
ci
)
:
_os
(
llvm
::
errs
())
,
_sm
(
ci
.
getSourceManager
())
,
_context
(
ci
.
getASTContext
())
,
_ci
(
ci
)
,
_systemcModel
(
nullptr
)
{
_os
{
llvm
::
errs
()
}
,
_sm
{
ci
.
getSourceManager
()
}
,
_context
{
ci
.
getASTContext
()
}
,
_ci
{
ci
}
,
_systemcModel
{
nullptr
}
{
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment