universal character name (arbitrary Unicode value); may result in several characters
code point U+nnnn
\Unnnnnnnn
universal character name (arbitrary Unicode value); may result in several characters
code point U+nnnnnnnn
For example:
The following source skips to the start of the next page. (Applies
mostly to terminals where the output device is a printer rather than a
VDU (Virtual Display Unit))
Global variables are powerful but have the risk of being altered carelessly. Under most cases, we can add static
modifier on this global variables such that these variables can only be
altered in the file. However, there do have some situations that we
have to use global variables across different files. In this case, we
usually encounter an error happened in linking phase, i.e., error:multiple definition.
Some of these errors are obvious and easily to debug while others can
be really puzzling. Here I will give you an example that I encountered.
Suppose we have two source files, and the content is
1
2
3
4
5
6
7
#include<stdio.h>
// main.c
int gvar;
intmain(){
printf("shared var is %d\n",gvar);
return0;
}
1
2
// aux.c
int gvar = 5;
then we run the command
1
gcc -o test main.c test.c
and everything goes smoothly. That is to say, the code presented is correct, variable gvar is not multiple definition. Note that there is no extern modifier for gvar and the result of this program is 5, which means that the variable gvar
is shared across two files. This is quite strange since we know that if
two global variable have the same name in a project, it will incur multiple definition errors.
What is more, I found something more interesting. When we change the postfix of these two files,
1
2
3
mv main.c main.cpp
mv test.c test.cpp
gcc -o test test.cpp main.cpp #error:multiple definition
the compiler told me that there is a multiple definition error for variable gvar.
How does this happen, the content in the files are not changed and we
only changed the file name. An intuitive explanation is that the
different of c++ and c cause the puzzling bug since .cpp is the file type for c++ and .c is for c.
Strong and weak symbols
Actually,
these strange phenomenons are all caused by one features provided by
GCC, called strong and weak symbols. For global variables, it was
divided into three types.
initialized to a non-zero value
initialized to zero
not initialized, just defined
In GCC, the first two types of global variables is called strong symbols that are store in .DATA and .BSS section. As for the third type, it is called weak symbols, and it is saved in .COMMON section.
There are three rules that must be followed for these variables
only one strong symbol is allowed with the same name
if there exists one strong symbol and several weak symbols, the weak symbols are overrode by strong symbols
if there exists several weak symbols, GCC will choose one that have the largest size (memory occupation).
Now we can clarify why the c version program can run without any errors. In aux.c, we define a strong symbol gvar and it is initialized to 5. In main.c, we only define the variable gvar, and it is a weak symbol. When we compile the program using GCC, the gvar in main.c is overrode by gvar in aux.c according to the second rule. Therefore, the program runs smoothly and the result is 5. If we change the main.c as follows, it will incur multiple definition also.
1
2
3
4
5
6
7
#include<stdio.h>
// main.c
int gvar=0; // this is a strong symbol
intmain(){
printf("shared var is %d\n",gvar);
return0;
}
Wait, there is still one puzzling problem left. Why the program incurs multiple definition error when the file name is changed ? Actually, when we change the file type from .c to .cpp, the GCC compiler will use the rules for c++
problem to compile this c program. Therefore, to answer this question,
we need to investigate the difference when GCC handle the strong/weak
symbol between .cpp and .c.
Here is my conclusion. For c program, if you define an global
variable and not initialize it, GCC will regard it as weak symbol.
However, for c++ program, the default type is strong variable. That is to say, for line int gvar; in main.cpp, it is a strong symbol. Since we have another strong symbol with the same name in aux.cpp, the compiler gives the error.
If you want to use weak symbol in a c++ program, you need to explicitly declare the variable is weak. For example, if we write a c++ program like this,
1
2
3
4
5
6
7
#include<stdio.h>
// main.cpp
int __attribute__((weak)) gvar=2;
intmain(){
printf("shared var is %d\n",gvar);
return0;
}
1
2
// aux.cpp
int gvar = 5;
the program will have the same behavior like the c version.
To avoid the bugs like that, we can use the -fno-common
option provided by GCC, it will regard all variables as strong symbols.
However, in some cases, we have to use weak symbols (see next section).
Therefore, we should develop a good coding habit. There are three rules
we can follow,
eliminate all global variables (hard)
add static modifier for global variables, provide interfaces for accesses (medium)
initialize all global variables, such as zero (easy)
Function of s. w. symbols
It
seems that we should use strong symbols instead of weak symbols in
programming, so why does GCC provide weak symbols? As far as I known,
weak symbols are useful for library functions. For example, if the
symbols in library are weak symbols, users can easily override some
library functions for personal objectives. What’s more, programmers can
declare some weak symbols of library functions. If the program is linked
with the library, program can provide more powerful features,
Otherwise, the program can still run without any errors. Here is a
simple example.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <pthread.h>
__attribute__((weak)) int pthread_create( pthread_t*, const pthread_attr_t*,
void*(*)(void*), void*);
int main()
{
if (pthread_create)
{
printf("This is multi-thread version!\n");
}
else
{
printf("This is single-thread version!\n");
}
return 0;
}
If the program is not linked with pthread library, it will run in single-thread mode. Otherwise, it can run in multi-thread mode.
Manage your global variables
If
you have to use global variables, here is an way to manage your global
variables in an comfortable way. Create two files called global_var.h and global_var.c. Declare all global variables using extern modifier in global_var.h. Initialize all global variables in global_var.c. For instance,
1
2
3
4
5
6
// global_var.h
#ifndef GLOBAL_VAR
#define GLOBAL_VAR
externint g_A;
externchar g_B;
#endif
1
2
3
// global_var.c
int g_A = 0;
char g_B = 'g';
When you need to use global variables in other files, such as main.c, simple include global_var.h and you will be able to access all global variables.
1
2
3
4
5
6
7
// main.c
#include<stdio.h>
#include<global_var.h>
intmain(){
printf("var is %d\n",g_A);
return0;
}
Through this way, you can easily manage your global variables. However, be sure to use global variables as less as possible.
Instructions
in a pipelined processor are performed in several stages, so that at
any given time several instructions are being processed in the various
stages of the pipeline, such as fetch and execute. There are many
different instruction pipeline microarchitectures, and instructions may be executed out-of-order. A hazard occurs when two or more of these simultaneous (possibly out of order) instructions conflict.
Types
Data hazards
Data hazards occur when instructions that exhibit data dependence modify data in different stages of a pipeline. Ignoring potential data hazards can result in race conditions (also termed race hazards). There are three situations in which a data hazard can occur:
read after write (RAW), a true dependency
write after read (WAR), an anti-dependency
write after write (WAW), an output dependency
Consider two instructions i1 and i2, with i1 occurring before i2 in program order.
Read after write (RAW)
(i2 tries to read a source before i1
writes to it) A read after write (RAW) data hazard refers to a
situation where an instruction refers to a result that has not yet been
calculated or retrieved. This can occur because even though an
instruction is executed after a prior instruction, the prior instruction
has been processed only partly through the pipeline.
Example
For example: i1. R2 <- R1 + R3
i2. R4 <- R2 + R3
The first instruction is calculating a value to be saved in register R2, and the second is going to use this value to compute a result for register R4. However, in a pipeline,
when operands are fetched for the 2nd operation, the results from the
first will not yet have been saved, and hence a data dependency occurs.
A data dependency occurs with instruction i2, as it is dependent on the completion of instruction i1.
Write after read (WAR)
(i2 tries to write a destination before it is read by i1) A write after read (WAR) data hazard represents a problem with concurrent execution.
Example
For example: i1. R4 <- R1 + R5
i2. R5 <- R1 + R2
In any situation with a chance that i2 may finish before i1 (i.e., with concurrent execution), it must be ensured that the result of register R5 is not stored before i1 has had a chance to fetch the operands.
Write after write (WAW)
(i2 tries to write an operand before it is written by i1) A write after write (WAW) data hazard may occur in a concurrent execution environment.
Example
For example: i1. R2 'R2 <- R4 + R7 i2. R2 <- R1 + R3
The write back (WB) of i2 must be delayed until i1 finishes executing.
Structural hazards
A
structural hazard occurs when a part of the processor's hardware is
needed by two or more instructions at the same time. A canonical example
is a single memory unit that is accessed both in the fetch stage where
an instruction is retrieved from memory, and the memory stage where data
is written and/or read from memory.[3] They can often be resolved by separating the component into orthogonal units (such as separate caches) or bubbling the pipeline.
Branching hazards (also termed control hazards) occur with branches.
On many instruction pipeline microarchitectures, the processor will not
know the outcome of the branch when it needs to insert a new
instruction into the pipeline (normally the fetch stage).
int foo(void) {
int start = __builtin_readcyclecounter();
CODE to be TESTED
int end = __builtin_readcyclecounter();
int diff = end - start
printf(diff);
}
xscvsxdsp XT,XB reset_xflags() src ← ConvertSDtoDP(VSR[32×BX+B].dword[0]) result ← RoundToSP(RN,src) VSR[32×TX+T].dword[0] ← ConvertSPtoSP64(result) VSR[32×TX+T].dword[1] ← 0xUUUU_UUUU_UUUU_UUUU if(xx_flag) then SetFX(XX) FPRF ← ClassSP(result) FR ← inc_flag FI ← xx_flag Let XT be the value 32×TX + T.
Let XB be the value 32×BX + B. Let src be the two’s-complement integer value in doubleword element 0 of VSR[XB]. src is converted to floating-point format, and rounded to single-precision using the rounding mode specified by RN. The result is placed into doubleword element 0 of VSR[XT] in double-precision format. The contents of doubleword element 1 of VSR[XT] are undefined. FPRF is set to the class and sign of the result as represented in single-precision format. FR is set to indicate if the result was incremented when rounded. FI is set to indicate the result is inexact. Special Registers Altered FPRF FR FI FX XX
if MSR.VSX=0 then VSX_Unavailable() EA ← ( (RA=0) ? 0 : GPR[RA] ) + GPR[RB] VSR[32×TX+T].dword[0] ← EXTS64(MEM(EA,4)) VSR[32×TX+T].dword[1] ← 0xUUUU_UUUU_UUUU_UUUU
Let XT be the value 32×TX + T. Let EA be the sum of the contents of GPR[RA], or 0 if RA is equal to 0, and the contents of GPR[RB]. When Big-Endian byte ordering is employed, the contents of the word in storage at address EA are placed into load_data in such an order that; – the contents of the byte in storage at address EA are placed into byte 0 of load_data, – the contents of the byte in storage at address EA+1 are placed into byte 1 of load_data, – the contents of the byte in storage at address EA+2 are placed into byte 2 of load_data, and – the contents of the byte in storage at address EA+3 are placed into byte 3 of load_data. When Little-Endian byte ordering is employed, the contents of the word in storage at address EA are placed into load_data in such an order that; – the contents of the byte in storage at address EA are placed into byte 3 of load_data, – the contents of the byte in storage at address EA+1 are placed into byte 2 of load_data, – the contents of the byte in storage at address EA+2 are placed into byte 1 of load_data, and – the contents of the byte in storage at address EA+3 are placed into byte 0 of load_data. load_data is sign-extended to a doubleword and placed in doubleword element 0 of VSR[XT]. The contents of doubleword element 1 of VSR[XT] are undefined. Special Registers Altered None
LLVM provides two different representation after generating the hardware instructions. The .s file (assembly representation) and .o file ( the object file). They are just two different "IR". If we want to generate the .o file from .s file, we need to call the assembler (usually /usr/bin/as). If we want to do the opposite, we can use objdump -d (meaning disassemble)
The assembler and disassembler together can be used to modify the assembly and do some testing.
e.g. :
clang a.c -S -O2 -o a.s
do some modifications to the a.s
clang a.s -o a.out (here clang implicitly invoke the assembler to generate exe)
\brief: DCBT is one of the Data Cache instruction used on PowerPC.
The Data Cache instructions control various aspects of the data cache. TH field in the dcbt and dcbtst instructions Described below are the TH field values for the dcbt and dcbtst instructions. For all TH field values which are not listed, the hint provided by the instruction is undefined. TH=0b00000 If TH=0b00000, the dcbt/dcbtst instruction provides a hint that the program will probably soon access the block containing the byte addressed by EA. TH=0b01000 - 0b01111 The dcbt/dcbtst instructions provide hints regarding a sequence of accesses to data elements, or indicate the expected use thereof. Such a sequence is called a “data stream”, and a dcbt/dcbtst instruction in which TH is set to one of these values is said to be a “data stream variant” of dcbt/dcbtst. In the remainder of this section, “data stream” may be abbreviated to “stream”. A data stream to which a program may perform Load accesses is said to be a “load data stream”, and is described using the data stream variants of the dcbt instruction. A data stream to which a program may perform Store accesses is said to be a “store data stream”, and is described using the data stream variants of the dcbtst instruction. When, and how often, effective addresses for a data stream are translated is implementation-dependent. Each data element is associated with a unit of storage,
If you are signed in as the root user, you can create a new user at any time by typing:
adduser newuser
If you are signed in as a non-root user who has been given sudo privileges, as demonstrated in the initial server setup guide, you can add a new user by typing:
X-form stxsihx XS,RA,RB Let XS be the value 32×SX + S. Let the effective address (EA) be sum of the contents of GPR[RA], or 0 if RA is equal to 0, and the contents of GPR[RB]. The contents of halfword element 3 of VSR[XS] are placed into the halfword in storage addressed by EA. Special Registers Altered: None 31 S RA RB 909 SX 0 6 11 16 21 31 if SX=0 & MSR.VSX=0 then VSX_Unavailable() if SX=1 & MSR.VEC=0 then Vector_Unavailable() EA <= ((RA=0) ? 0 : GPR[RA]) + GPR[RB] MEM(EA,1) <= VSR[32×SX+S].byte[7] VSR Data Layout for stxsibx src = VSR[XS] unused .byte unused 0 56 64 127 31 S RA RB 941 SX 0 6 11 16 21 31 if SX=0 & MSR.VSX=0 then VSX_Unavailable() if SX=1 & MSR.VEC=0 then Vector_Unavailable() EA <= ((RA=0) ? 0 : GPR[RA]) + GPR[RB] MEM(EA,2) <= VSR[32×SX+S].hword[3] VSR Data Layout for stxsihx src = VSR[XS] unused .hword[3] unused 0 48 64 127
Similarly STXSIBX, just store byte 7 into EA address in memory.
VSSRC 32 VSFRC 64 VSRC 128 VRRC 128 F4RC 32 F8RC 64 VSHRC 128 we have 64 VSR’s (128-bit vector registers). The first 32 of them overlap with the FPR’s (floating point registers that occupy the most significant 64 bits of each). The last 32 of them overlap with VR’s (128-bit Altivec/VMX registers).
- VSSRC(Vector-Scalar, Single-precision) is a scalar register class consisting of 64 registers that can each hold an f32 (edited)
- VSFRC (Vector Scalar Floating-points) class consisting of 64 registers that can each hold an f64
- VSRC is a register class consisting of 64 registers that can each hold a vector. This is currently restricted to `v4i32, v4f32, v2i64, v2f64` since those are the only types that we have meaningful operations on in the ISA.
And VSSRC and VSFRC are the upper 64-bits of VSR0-VSR63. I.e. they model higher 64 bit of VSR (all 64 of them)
- VRRC is a register class consisting of 32 registers that each hold an Altivec (VMX) vector type which is VSR32-VSR63
- F4RC is just the 32 floating point registers used for f32 values
So F4RC models the upper 64 bit of VSR0-VSR31? and for 32 floating point
- F8RC are the 32 floating point registers that each hold an f64
Then we have a bit of complexity when it comes to GPRC/G8RC. On the one hand, it’s pretty simple - 32/64 bit integer registers respectively. R0-R31 are 32-bit ones. X0-X31 are the 64-bit ones. However, we have a number of instructions that treat R0/X0 as special - a zero means immediate zero, not contents of register zero. For such instructions we use the `GPRC_NOR0` and `G8RC_NOX0`. They have a special register called `ZERO` and `ZERO8` respectively. And what we do is mark this register as reserved. Then we scan the instructions that use this register class to see if they’re fed by a register that contains a zero, we get rid of the instruction that defines that register and we allocate `ZERO/ZERO8` because they instructions need constant 0 instead of R0
So we’ll catch stuff like this: ```li 4, 0 lfsx 2, 4, 3 ``` we can freely get rid of the first instruction and put a `ZERO` instead of 4 in the `lfsx`
If you use the wrong register class, you could get werid SDAG selcetion error. (Can not map REGA to REGB, or something) FPR(i) = upper VSR(i) isCodeGenOnly means not suitable for disassembly and assembly etc.
Error for using wrong register class: : error: In XXBRH: Type inference contradiction found, merging '{v4i32:v2i 64:v4f32:v2f64}' into 'v8i16' def XXBRH : XX2_XT6_XO5_XB6<60, 7, 475, "xxbrh", vsrc,
The COPY_TO_REGCLASS must be used, if you want to copy a value to a class that doesn't support that type. e.g. $A is v1i128, which is not supported by VSRC, thus we used COPY_TO_REGCLASS, we also copied the result of XXBRQ to VRRC because after exectuting XXBRQ the result is still v1i128 even though you COPY_TO_REGCLASS $A to it, and VRRC the only register class that support v1i128. (v1i128 (COPY_TO_REGCLASS (XXBRQ (COPY_TO_REGCLASS $A, VSRC)), VRRC))>;
a) put the file name in to a .sh, each line just put one file name, and make sure there is no empty line. b) :%s#\(.*\)#rename \1 prefix_\1_suffix#g
or b) :%s#\(.*\)#mv \1 prefix_\1_suffix#g (if you get error: Bareword "some_word" not allowed while "strict subs" in use at (eval 1) line number.)
c) edit the name to what you like, e.g, here I have to change *_119793 to *, I first change them all to *_1197938888, and then search _1197938888 and delete these suffix all.
d) then change mode to the script and run it. (chmod 777 rename.sh)
Say you have a vector int a = {1,2,3,4}, vector int b = {5,6,7,8} The lay out of a in the memory on a BE machine is pretty simply, just as it is: Because BE means high word is save in low address ( but it is natural for array access) Addr0x 0 1 2 3 BE: [1,2,3,4] On LE it is the opposite (high word is save in high address): Addr0x 0 1 2 3 LE: [4,3,2,1]
say you have xxsldwi(a,b,3) On BE: it is just 0 1 2 3 4 5 6 7 [1,2,3,4] [5,6,7,8] The Result RT = {4,5,6,7} if you want to use vec_shuffle to implement xxsldwi, you have to pass in shuffle_vector((vector int)a, (vector int)b, 3,4,5,6), you can simply image BE is acess from Left to right. if you do for (int i = 0; i < 4; i++) { printf("RT[%d]=%d\n\n",i, RT[i]); } it is just : c[0]=4
c[1]=5
c[2]=6
c[3]=7
But On LE, for the same array a and b, it is just: 3 2 1 0 7 6 5 4 [4,3,2,1] [8,7,6,5]
The Result RT = [1,8,7,6] in the register So no matter what, when xxsldwi(a,b,3), the word[3] of A, followed by word[0], word[1] and word[2] will be put into the result vector register if you want to use vec_shuffle to implement xxsldwi, you have to pass in shuffle_vector((vector int)a, (vector int)b,5,6,7,0), you can simply image LE is acess from right to left. if you do for (int i = 0; i < 4; i++) { printf("RT[%d]=%d\n\n",i, RT[i]); } it is just : c[0]=6