Commit cbf8434e authored by Xavier Leroy's avatar Xavier Leroy
Browse files

Improvements in the StructReturn transformation (ABI conformance for passing composites).

- Implement the "1/2/4/8" composite return policy, used by IA32/MacOS X and IA32/BSD.
- Move the default passing conventions from Machine.ml to compcert.ini, making it easier to test the various conventions.
- More comprehensive interoperability test in regression/interop1.c.
parent 42e397bd
......@@ -203,6 +203,8 @@ compcert.ini: Makefile.config VERSION
echo "system=$(SYSTEM)"; \
echo "has_runtime_lib=$(HAS_RUNTIME_LIB)"; \
echo "asm_supports_cfi=$(ASM_SUPPORTS_CFI)"; \
echo "struct_passing_style=$(STRUCT_PASSING)"; \
echo "struct_return_style=$(STRUCT_RETURN)"; \
version=`cat VERSION`; \
echo version=$$version) \
> compcert.ini
......
......@@ -79,14 +79,18 @@ done
cchecklink=false
casmruntime=""
asm_supports_cfi=""
struct_passing=""
struct_return=""
case "$target" in
powerpc-linux|ppc-linux|powerpc-eabi|ppc-eabi)
arch="powerpc"
model="standard"
abi="eabi"
struct_passing="ref-caller"
case "$target" in
*-linux) abi="linux";;
*-eabi) abi="eabi";;
*-linux) struct_return="ref";;
*-eabi) struct_return="int1-8";;
esac
system="linux"
cc="${toolprefix}gcc"
......@@ -100,6 +104,8 @@ case "$target" in
arch="powerpc"
model="standard"
abi="eabi"
struct_passing="ref-caller"
struct_return="int1-8"
system="diab"
cc="${toolprefix}dcc"
cprepro="${toolprefix}dcc -E"
......@@ -128,6 +134,8 @@ case "$target" in
echo "$usage" 1>&2
exit 2;;
esac
struct_passing="ints"
struct_return="int1-4"
system="linux"
cc="${toolprefix}gcc"
cprepro="${toolprefix}gcc -U__GNUC__ '-D__REDIRECT(name,proto,alias)=name proto' '-D__REDIRECT_NTH(name,proto,alias)=name proto' -E"
......@@ -138,6 +146,8 @@ case "$target" in
arch="ia32"
model="sse2"
abi="standard"
struct_passing="ints"
struct_return="ref"
system="linux"
cc="${toolprefix}gcc -m32"
cprepro="${toolprefix}gcc -m32 -U__GNUC__ -E"
......@@ -148,6 +158,8 @@ case "$target" in
arch="ia32"
model="sse2"
abi="standard"
struct_passing="ints"
struct_return="int1248" # to check!
system="bsd"
cc="${toolprefix}gcc -m32"
cprepro="${toolprefix}gcc -m32 -U__GNUC__ -E"
......@@ -158,6 +170,8 @@ case "$target" in
arch="ia32"
model="sse2"
abi="macosx"
struct_passing="ints"
struct_return="int1248"
system="macosx"
cc="${toolprefix}gcc -arch i386"
cprepro="${toolprefix}gcc -arch i386 -U__GNUC__ -U__clang__ -U__BLOCKS__ '-D__attribute__(x)=' '-D__asm(x)=' -E"
......@@ -173,6 +187,8 @@ case "$target" in
arch="ia32"
model="sse2"
abi="standard"
struct_passing="ints"
struct_return="ref"
system="cygwin"
cc="${toolprefix}gcc -m32"
cprepro="${toolprefix}gcc -m32 -U__GNUC__ -E"
......@@ -329,6 +345,8 @@ cat >> Makefile.config <<EOF
ARCH=$arch
MODEL=$model
ABI=$abi
STRUCT_PASSING=$struct_passing
STRUCT_RETURN=$struct_return
SYSTEM=$system
CC=$cc
CPREPRO=$cprepro
......@@ -365,9 +383,19 @@ MODEL=
# ABI=standard # for IA32
ABI=
# Default calling conventions for passing structs and unions by value
# See options -fstruct-passing=<style> and -fstruct-return=<style>
# in the CompCert user's manual
STRUCT_PASSING=ref_callee
# STRUCT_PASSING=ref_caller
# STRUCT_PASSING=ints
STRUCT_RETURN=ref
# STRUCT_RETURN=int1248
# STRUCT_RETURN=int1-4
# STRUCT_RETURN=int1-8
# Target operating system and development environment
# Possible choices for PowerPC:
# SYSTEM=macosx
# SYSTEM=linux
# SYSTEM=diab
# Possible choices for ARM:
......@@ -428,6 +456,7 @@ CompCert configuration:
Target architecture........... $arch
Hardware model................ $model
Application binary interface.. $abi
Composite passing conventions. arguments: $struct_passing, return values: $struct_return
OS and development env........ $system
C compiler.................... $cc
C preprocessor................ $cprepro
......
......@@ -44,16 +44,9 @@ type t = {
alignof_fun: int option;
bigendian: bool;
bitfields_msb_first: bool;
supports_unaligned_accesses: bool;
struct_return_as_int: int;
struct_passing_style: struct_passing_style
supports_unaligned_accesses: bool
}
and struct_passing_style =
| SP_ref_callee
| SP_ref_caller
| SP_split_args
let ilp32ll64 = {
name = "ilp32ll64";
char_signed = false;
......@@ -83,9 +76,7 @@ let ilp32ll64 = {
alignof_fun = None;
bigendian = false;
bitfields_msb_first = false;
supports_unaligned_accesses = false;
struct_return_as_int = 0;
struct_passing_style = SP_ref_callee
supports_unaligned_accesses = false
}
let i32lpll64 = {
......@@ -117,9 +108,7 @@ let i32lpll64 = {
alignof_fun = None;
bigendian = false;
bitfields_msb_first = false;
supports_unaligned_accesses = false;
struct_return_as_int = 0;
struct_passing_style = SP_ref_callee
supports_unaligned_accesses = false
}
let il32pll64 = {
......@@ -151,9 +140,7 @@ let il32pll64 = {
alignof_fun = None;
bigendian = false;
bitfields_msb_first = false;
supports_unaligned_accesses = false;
struct_return_as_int = 0;
struct_passing_style = SP_ref_callee
supports_unaligned_accesses = false
}
(* Canned configurations for some ABIs *)
......@@ -163,12 +150,10 @@ let x86_32 =
char_signed = true;
alignof_longlong = 4; alignof_double = 4;
sizeof_longdouble = 12; alignof_longdouble = 4;
supports_unaligned_accesses = true;
struct_passing_style = SP_split_args }
supports_unaligned_accesses = true }
let x86_32_macosx =
{ x86_32 with sizeof_longdouble = 16; alignof_longdouble = 16;
struct_return_as_int = 8 }
{ x86_32 with sizeof_longdouble = 16; alignof_longdouble = 16 }
let x86_64 =
{ i32lpll64 with name = "x86_64"; char_signed = true }
......@@ -183,17 +168,10 @@ let ppc_32_bigendian =
{ ilp32ll64 with name = "powerpc";
bigendian = true;
bitfields_msb_first = true;
supports_unaligned_accesses = true;
struct_return_as_int = 8;
struct_passing_style = SP_ref_caller }
let ppc_32_bigendian_linux =
{ ppc_32_bigendian with struct_return_as_int = 0 }
supports_unaligned_accesses = true }
let arm_littleendian =
{ ilp32ll64 with name = "arm";
struct_return_as_int = 4;
struct_passing_style = SP_split_args }
{ ilp32ll64 with name = "arm" }
(* Add GCC extensions re: sizeof and alignof *)
......@@ -205,9 +183,7 @@ let gcc_extensions c =
let compcert_interpreter c =
{ c with sizeof_longdouble = 8; alignof_longdouble = 8;
supports_unaligned_accesses = false;
struct_return_as_int = 0;
struct_passing_style = SP_ref_callee }
supports_unaligned_accesses = false }
(* Undefined configuration *)
......@@ -240,9 +216,7 @@ let undef = {
alignof_fun = None;
bigendian = false;
bitfields_msb_first = false;
supports_unaligned_accesses = false;
struct_return_as_int = 0;
struct_passing_style = SP_ref_callee
supports_unaligned_accesses = false
}
(* The current configuration. Must be initialized before use. *)
......
......@@ -44,16 +44,9 @@ type t = {
alignof_fun: int option;
bigendian: bool;
bitfields_msb_first: bool;
supports_unaligned_accesses: bool;
struct_return_as_int: int;
struct_passing_style: struct_passing_style
supports_unaligned_accesses: bool
}
and struct_passing_style =
| SP_ref_callee (* by reference, callee takes copy *)
| SP_ref_caller (* by reference, caller takes copy *)
| SP_split_args (* by value, as a sequence of ints *)
(* The current configuration *)
val config : t ref
......@@ -69,7 +62,6 @@ val x86_64 : t
val win32 : t
val win64 : t
val ppc_32_bigendian : t
val ppc_32_bigendian_linux : t
val arm_littleendian : t
val gcc_extensions : t -> t
......
......@@ -18,11 +18,12 @@
- passed by value as function parameters. *)
open Machine
open Configuration
open C
open Cutil
open Transform
let struct_return_style = ref 0
let struct_return_style = ref SR_ref
let struct_passing_style = ref SP_ref_callee
(* Classification of function return types. *)
......@@ -38,11 +39,18 @@ let classify_return env ty =
if is_composite_type env ty then begin
match sizeof env ty, alignof env ty with
| Some sz, Some al ->
if !struct_return_style >= 4 && sz <= 4 then
Ret_value (TInt(IUInt, []), sz, al)
else if !struct_return_style >= 8 && sz <= 8 then
Ret_value (TInt(IULongLong, []), sz, al)
else Ret_ref
begin match !struct_return_style with
| SR_int1248 when sz = 1 || sz = 2 || sz = 4 ->
Ret_value (TInt(IUInt, []), sz, al)
| SR_int1248 when sz = 8 ->
Ret_value (TInt(IULongLong, []), sz, al)
| (SR_int1to4 | SR_int1to8) when sz <= 4 ->
Ret_value (TInt(IUInt, []), sz, al)
| SR_int1to8 when sz > 4 && sz <= 8 ->
Ret_value (TInt(IULongLong, []), sz, al)
| _ ->
Ret_ref
end
| _, _ ->
Ret_ref (* should not happen *)
end else
......@@ -85,6 +93,7 @@ let ulonglong = TInt(IULongLong, [])
let ucharptr = TPtr(uchar, [])
let ushortptr = TPtr(ushort, [])
let uintptr = TPtr(uint, [])
let ulonglongptr = TPtr(ulonglong, [])
let ty_buffer n =
TArray(uint, Some (Int64.of_int n), [])
......@@ -110,6 +119,7 @@ let lshift a nbytes =
etyp = uint }
let offsetptr base ofs =
if ofs = 0 then base else
{ edesc = EBinop(Oadd, base, intconst (Int64.of_int ofs) IInt, ucharptr);
etyp = ucharptr }
......@@ -127,6 +137,10 @@ let load4 base ofs =
let a = ecast uintptr (offsetptr base ofs) in
{ edesc = EUnop(Oderef, a); etyp = uint }
let load8 base ofs =
let a = ecast ulonglongptr (offsetptr base ofs) in
{ edesc = EUnop(Oderef, a); etyp = ulonglong }
let lshift_ll a nbytes =
let a = ecast ulonglong a in
if nbytes = 0 then a else
......@@ -172,14 +186,16 @@ let rec load_words base ofs sz al =
else load_word base ofs 4 al :: load_words base (ofs + 4) sz al
let load_result base sz al =
assert (sz <= 8);
if sz <= 4 then
load_word base 0 sz al
else if sz <= 8 then begin
else if sz = 8 && (al >= 8 || (!config).supports_unaligned_accesses) then
load8 base 0
else begin
let (shift1, shift2) = if (!config).bigendian then (4, 0) else (0, 4) in
or2_ll (lshift_ll (load_word base 0 4 al) shift1)
(lshift_ll (load_word base 4 (sz - 4) al) shift2)
end else
assert false
end
(* Rewriting of function types. For the return type:
return kind scalar -> no change
......@@ -368,7 +384,8 @@ and transf_arguments env args =
let ty' = transf_type env arg.etyp in
if translates_to_extended_lvalue arg then begin
let tmp = new_temp ~name:"_arg" ucharptr in
(eassign tmp (eaddrof (transf_expr env Val arg)) :: assignments,
(eassign tmp (ecast ucharptr (eaddrof (transf_expr env Val arg)))
:: assignments,
load_words tmp 0 sz al @ args')
end else begin
let tmp = new_temp ~name:"_arg" (ty_buffer n) in
......@@ -452,7 +469,7 @@ let rec transf_stmt s =
if translates_to_extended_lvalue e then begin
let tmp = new_temp ~name:"_res" ucharptr in
sseq s.sloc
(sassign s.sloc tmp (eaddrof e'))
(sassign s.sloc tmp (ecast ucharptr (eaddrof e')))
{sdesc = Sreturn (Some (load_result tmp sz al)); sloc = s.sloc}
end else begin
let dst = new_temp ~name:"_res" ty in
......@@ -553,17 +570,13 @@ let transf_composite env su id attr fl =
let program p =
struct_passing_style :=
if !Clflags.option_interp then SP_ref_callee else
begin match !Clflags.option_fstruct_passing_style with
| Some st -> st
| None -> (!config).struct_passing_style
end;
if !Clflags.option_interp
then SP_ref_callee
else !Clflags.option_fstruct_passing_style;
struct_return_style :=
if !Clflags.option_interp then 0 else
begin match !Clflags.option_fstruct_return_style with
| Some st -> st
| None -> (!config).struct_return_as_int
end;
if !Clflags.option_interp
then SR_ref
else !Clflags.option_fstruct_return_style;
Transform.program
~decl:transf_decl
~fundef:transf_fundef
......
......@@ -17,8 +17,8 @@ let linker_options = ref ([]: string list)
let assembler_options = ref ([]: string list)
let option_flongdouble = ref false
let option_fstruct_return = ref false
let option_fstruct_return_style = ref (None: int option)
let option_fstruct_passing_style = ref (None: Machine.struct_passing_style option)
let option_fstruct_return_style = ref Configuration.struct_return_style
let option_fstruct_passing_style = ref Configuration.struct_passing_style
let option_fbitfields = ref false
let option_fvararg_calls = ref true
let option_funprototyped = ref true
......
......@@ -94,3 +94,30 @@ let asm_supports_cfi =
| v -> bad_config "asm_supports_cfi" [v]
let version = get_config_string "version"
type struct_passing_style =
| SP_ref_callee (* by reference, callee takes copy *)
| SP_ref_caller (* by reference, caller takes copy *)
| SP_split_args (* by value, as a sequence of ints *)
type struct_return_style =
| SR_int1248 (* return by content if size is 1, 2, 4 or 8 bytes *)
| SR_int1to4 (* return by content if size is <= 4 *)
| SR_int1to8 (* return by content if size is <= 8 *)
| SR_ref (* always return by assignment to a reference
given as extra argument *)
let struct_passing_style =
match get_config_string "struct_passing_style" with
| "ref-callee" -> SP_ref_callee
| "ref-caller" -> SP_ref_caller
| "ints" -> SP_split_args
| v -> bad_config "struct_passing_style" [v]
let struct_return_style =
match get_config_string "struct_return_style" with
| "int1248" -> SR_int1248
| "int1-4" -> SR_int1to4
| "int1-8" -> SR_int1to8
| "ref" -> SR_ref
| v -> bad_config "struct_return_style" [v]
(* *********************************************************************)
(* *)
(* The Compcert verified compiler *)
(* *)
(* Bernhard Schommer, AbsInt Angewandte Informatik GmbH *)
(* *)
(* AbsInt Angewandte Informatik GmbH. All rights reserved. This file *)
(* is distributed under the terms of the INRIA Non-Commercial *)
(* License Agreement. *)
(* *)
(* *********************************************************************)
val arch: string
(** Target architecture *)
val model: string
(** Sub-model for this architecture *)
val abi: string
(** ABI to use *)
val system: string
(** Flavor of operating system that runs CompCert *)
val prepro: string list
(** How to invoke the external preprocessor *)
val asm: string list
(** How to invoke the external assembler *)
val linker: string list
(** How to invoke the external linker *)
val asm_supports_cfi: bool
(** True if the assembler supports Call Frame Information *)
val stdlib_path: string
(** Path to CompCert's library *)
val has_runtime_lib: bool
(** True if CompCert's library is available. *)
val version: string
(** CompCert version string *)
type struct_passing_style =
| SP_ref_callee (* by reference, callee takes copy *)
| SP_ref_caller (* by reference, caller takes copy *)
| SP_split_args (* by value, as a sequence of ints *)
type struct_return_style =
| SR_int1248 (* return by content if size is 1, 2, 4 or 8 bytes *)
| SR_int1to4 (* return by content if size is <= 4 *)
| SR_int1to8 (* return by content if size is <= 8 *)
| SR_ref (* always return by assignment to a reference
given as extra argument *)
val struct_passing_style: struct_passing_style
(** Calling conventions to use for passing structs and unions as
first-class values *)
val struct_return_style: struct_return_style
(** Calling conventions to use for returning structs and unions as
first-class values *)
......@@ -558,22 +558,25 @@ let cmdline_actions =
Exact "-all", Self (fun _ -> Interp.mode := Interp.All);
(* Special -f options *)
Exact "-fstruct-passing=ref-callee",
Self (fun _ -> option_fstruct_passing_style := Some Machine.SP_ref_callee);
Self (fun _ -> option_fstruct_passing_style := Configuration.SP_ref_callee);
Exact "-fstruct-passing=ref-caller",
Self (fun _ -> option_fstruct_return := true;
option_fstruct_passing_style := Some Machine.SP_ref_caller);
option_fstruct_passing_style := Configuration.SP_ref_caller);
Exact "-fstruct-passing=ints",
Self (fun _ -> option_fstruct_return := true;
option_fstruct_passing_style := Some Machine.SP_split_args);
option_fstruct_passing_style := Configuration.SP_split_args);
Exact "-fstruct-return=ref",
Self (fun _ -> option_fstruct_return := true;
option_fstruct_return_style := Some 0);
Exact "-fstruct-return=int4",
option_fstruct_return_style := Configuration.SR_ref);
Exact "-fstruct-return=int1248",
Self (fun _ -> option_fstruct_return := true;
option_fstruct_return_style := Some 4);
Exact "-fstruct-return=int8",
option_fstruct_return_style := Configuration.SR_int1248);
Exact "-fstruct-return=int1-4",
Self (fun _ -> option_fstruct_return := true;
option_fstruct_return_style := Some 8)
option_fstruct_return_style := Configuration.SR_int1to4);
Exact "-fstruct-return=int1-8",
Self (fun _ -> option_fstruct_return := true;
option_fstruct_return_style := Configuration.SR_int1to8)
]
(* -f options: come in -f and -fno- variants *)
(* Language support options *)
......@@ -628,9 +631,7 @@ let _ =
Printexc.record_backtrace true;
Machine.config :=
begin match Configuration.arch with
| "powerpc" -> if Configuration.abi = "linux"
then Machine.ppc_32_bigendian_linux
else Machine.ppc_32_bigendian
| "powerpc" -> Machine.ppc_32_bigendian
| "arm" -> Machine.arm_littleendian
| "ia32" -> if Configuration.abi = "macosx"
then Machine.x86_32_macosx
......
......@@ -366,7 +366,6 @@ let do_printf m fmt args =
let (>>=) opt f = match opt with None -> None | Some arg -> f arg
(*
(* Like eventval_of_val, but accepts static globals as well *)
let convert_external_arg ge v t =
......@@ -386,7 +385,6 @@ let rec convert_external_args ge vl tl =
convert_external_arg ge v1 t1 >>= fun e1 ->
convert_external_args ge vl tl >>= fun el -> Some (e1 :: el)
| _, _ -> None
*)
let do_external_function id sg ge w args m =
match extern_atom id, args with
......@@ -394,12 +392,8 @@ let do_external_function id sg ge w args m =
extract_string m b ofs >>= fun fmt ->
print_string (do_printf m fmt args');
flush stdout;
Some(((w, [Event_syscall(id, [], EVint Int.zero)]), Vint Int.zero), m)
(*
convert_external_args ge args sg.sig_args >>= fun eargs ->
Some(((w, [Event_syscall(id, eargs, EVint Int.zero)]), Vint Int.zero), m)
*)
| _ ->
None
......
--- CompCert calling native:
s1 = { a = 'a' }
s2 = { a = 'x', b = 'y' }
s3 = { a = 'a', b = 'b', c = ' c' }
s4 = { a = 'p', b = 'q', c = ' r', d = 's' }
s5 = { a = 'a', b = 'b', c = ' c', d = 'd', e = 'e' }
s6 = { a = 'a', b = 'b', c = ' c', d = 'd', e = 'e', f = 'f' }
s7 = { a = 'a', b = 'b', c = ' c', d = 'd', e = 'e', f = 'f', g = 'g' }
s8 = "Hello, world!"
t1 = { a = 12 }
t2 = { a = 34, b = 56 }
t3 = { a = -1, b = -2, c = -3 }
t4 = { a = 11, b = 22, c = 33, d = 44 }
t4 = { a = 1, b = 2, c = 3, d = 4, e = 'x' }
u1 = { a = 12345678 }
u2 = { a = 1, b = -1 }
u3 = { a = -1, b = -2, c = -3 }
u4 = { a = 4, b = 3, c = 2, d = 1 }
u5 = { a = 123, b = 'z' }
u6 = { a = -12345678, b = 555 }
u7 = { a = 111111111, b = 2222, c = 'a' }
u8 = { a = 'u', b = 8 }
u9 = { a = { 9, 8, 7, 6 } }
s1: { a = 'a' }
s2: { a = 'a', b = 'b' }
s3: { a = 'a', b = 'b', c = ' c' }
s4: { a = 'a', b = 'b', c = ' c', d = 'd' }
s5: { a = 'a', b = 'b', c = ' c', d = 'd', e = 'e' }
s6: { a = 'a', b = 'b', c = ' c', d = 'd', e = 'e', f = 'f' }
s7: { a = 'a', b = 'b', c = ' c', d = 'd', e = 'e', f = 'f', g = 'g' }
s8: "Hello world!"
t1: { a = 123 }
t2: { a = 123, b = 456 }
t3: { a = 123, b = 456, c = 789 }
t4: { a = 123, b = 456, c = 789, d = -111 }
t5: { a = 123, b = 456, c = 789, d = -999, e = 'x' }
u1: { a = 12 }
u2: { a = 12, b = -34 }
u3: { a = 12, b = 34, c = -56 }
u4: { a = 12, b = 34, c = 56, d = -78 }
u5: { a = 1234, b = 'u' }
u6: { a = 55555, b = 666 }
u7: { a = -10001, b = -789, c = 'z' }
u8: { a = 'x', b = 12345 }
after ms4, x = { 's', 'a', 'm', 'e' }
after mu9, x = { a = { 11, 22, 33, 44 } }
rs1 = { a = 'a' }
rs2 = { a = 'a', b = 'b' }
rs3 = { a = 'a', b = 'b', c = 'c' }
rs4 = { a = 'a', b = 'b', c = 'c', d = 'd' }
rs8 = { "Lorem ipsum" }
ru2 = { a = 12, b = -34 }
ru6 = { a = 12345678, b = -9999 }
ru9 = { a = { 111, 222, 333, 444 } }
after mu4, x = { a = { 11, 22, 33, 44 } }
rs1: { a = 'a' }
rs2: { a = 'a', b = 'b' }
rs3: { a = 'a', b = 'b', c = ' c' }
rs4: { a = 'a', b = 'b', c = ' c', d = 'd' }
rs5: { a = 'a', b = 'b', c = ' c', d = 'd', e = 'e' }
rs6: { a = 'a', b = 'b', c = ' c', d = 'd', e = 'e', f = 'f' }
rs7: { a = 'a', b = 'b', c = ' c', d = 'd', e = 'e', f = 'f', g = 'g' }
rs8: "Hello world!"
rt1: { a = 123 }
rt2: { a = 123, b = 456 }
rt3: { a = 123, b = 456, c = 789 }
rt4: { a = 123, b = 456, c = 789, d = -111 }
rt5: { a = 123, b = 456, c = 789, d = -999, e = 'x' }
ru1: { a = 12 }
ru2: { a = 12, b = -34 }
ru3: { a = 12, b = 34, c = -56 }
ru4: { a = 12, b = 34, c = 56, d = -78 }
ru5: { a = 1234, b = 'u' }
ru6: { a = 55555, b = 666 }
ru7: { a = -10001, b = -789, c = 'z' }
ru8: { a = 'x', b = 12345 }
--- native calling CompCert:
s1 = { a = 'a' }
s2 = { a = 'x', b = 'y' }
s3 = { a = 'a', b = 'b', c = ' c' }
s4 = { a = 'p', b = 'q', c = ' r', d = 's' }
s5 = { a = 'a', b = 'b', c = ' c', d = 'd', e = 'e' }