From 55937c177f90ecf0dea40c318d2f8d52fa69b55d Mon Sep 17 00:00:00 2001 From: Bernhard Schommer Date: Fri, 20 Jan 2017 10:55:26 +0100 Subject: Implement offsetof via builtin. The implementation of offsetof as macro in the form ((size_t) &((ty*) NULL)->member) has the problem that it cannot be used everywhere were an integer constant expression is allowed, for example in initiliazers of global variables and there is also no check for the case that member is of bitifield type. The new implementation adds a builtin function for this which is replaced by an integer constant during elaboration. Bug 20765 --- cparser/Elab.ml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'cparser/Elab.ml') diff --git a/cparser/Elab.ml b/cparser/Elab.ml index 1bfc2d11..d8d1d7d2 100644 --- a/cparser/Elab.ml +++ b/cparser/Elab.ml @@ -1642,6 +1642,22 @@ let elab_expr vararg loc env a = error "invalid application of 'alignof' to an incomplete type %a" (print_typ env) ty; { edesc = EAlignof ty; etyp = TInt(size_t_ikind(), []) },env' + | BUILTIN_OFFSETOF ((spec,dcl), mem) -> + let (ty,env) = elab_type loc env spec dcl in + let offset = + match unroll env ty with + | TStruct(id, attrs) -> + if Cutil.incomplete_type env ty then + error "offsetof of incomplete type %a" (print_typ env) ty; + let fld = (wrap Env.find_struct_member loc env (id,mem)) in + if List.exists (fun fld -> fld.fld_bitfield <> None) fld then + error "cannot compute the offset of bitfield '%s" mem; + Cutil.offsetof env ty fld + | _ -> + error "request offsetof for member '%s' in something not a structure" mem in + let offsetof_const = EConst (CInt(Int64.of_int offset,size_t_ikind (),"")) in + { edesc = offsetof_const; etyp = TInt(size_t_ikind(), []) },env + | UNARY(PLUS, a1) -> let b1,env = elab env a1 in if not (is_arith_type env b1.etyp) then -- cgit From ac2e4bb9bc63e0ea8b5cf67274bddb9ec74b771e Mon Sep 17 00:00:00 2001 From: Bernhard Schommer Date: Fri, 20 Jan 2017 12:05:15 +0100 Subject: Also support union. Bug 20765 --- cparser/Elab.ml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'cparser/Elab.ml') diff --git a/cparser/Elab.ml b/cparser/Elab.ml index d8d1d7d2..afdf3969 100644 --- a/cparser/Elab.ml +++ b/cparser/Elab.ml @@ -1644,11 +1644,12 @@ let elab_expr vararg loc env a = | BUILTIN_OFFSETOF ((spec,dcl), mem) -> let (ty,env) = elab_type loc env spec dcl in + if Cutil.incomplete_type env ty then + error "offsetof of incomplete type %a" (print_typ env) ty; let offset = match unroll env ty with - | TStruct(id, attrs) -> - if Cutil.incomplete_type env ty then - error "offsetof of incomplete type %a" (print_typ env) ty; + | TStruct(id,_) + | TUnion (id,_)-> let fld = (wrap Env.find_struct_member loc env (id,mem)) in if List.exists (fun fld -> fld.fld_bitfield <> None) fld then error "cannot compute the offset of bitfield '%s" mem; -- cgit From 47e818992372c1480b1052b64728a33d758637cf Mon Sep 17 00:00:00 2001 From: Bernhard Schommer Date: Fri, 20 Jan 2017 14:44:59 +0100 Subject: Simplified version. The problem was that sub structs are were not correctly aligned. The new version is much simpler and uses the sizeof_struct to calculate the individual offsets and add them up to get correct offest. Bug 20765 --- cparser/Elab.ml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'cparser/Elab.ml') diff --git a/cparser/Elab.ml b/cparser/Elab.ml index afdf3969..6256bf1f 100644 --- a/cparser/Elab.ml +++ b/cparser/Elab.ml @@ -1646,16 +1646,14 @@ let elab_expr vararg loc env a = let (ty,env) = elab_type loc env spec dcl in if Cutil.incomplete_type env ty then error "offsetof of incomplete type %a" (print_typ env) ty; - let offset = - match unroll env ty with - | TStruct(id,_) - | TUnion (id,_)-> - let fld = (wrap Env.find_struct_member loc env (id,mem)) in - if List.exists (fun fld -> fld.fld_bitfield <> None) fld then - error "cannot compute the offset of bitfield '%s" mem; - Cutil.offsetof env ty fld - | _ -> - error "request offsetof for member '%s' in something not a structure" mem in + let fld = match unroll env ty with + | TStruct(id,_) ->(wrap Env.find_struct_member loc env (id,mem)) + | TUnion (id,_)->(wrap Env.find_union_member loc env (id,mem)) + | _ -> + error "request offsetof for member '%s' in something not a structure" mem in + if List.exists (fun fld -> fld.fld_bitfield <> None) fld then + error "cannot compute the offset of bitfield '%s" mem; + let offset = Cutil.offsetof env ty fld in let offsetof_const = EConst (CInt(Int64.of_int offset,size_t_ikind (),"")) in { edesc = offsetof_const; etyp = TInt(size_t_ikind(), []) },env -- cgit From d60b593c8b1d19a4adfdadaeeaa93aa10b9dba53 Mon Sep 17 00:00:00 2001 From: Bernhard Schommer Date: Tue, 24 Jan 2017 10:29:30 +0100 Subject: New version to support designators. The c standard allows member designators for offsetof. The current implementation works by recursively combining the offset of each of the member designators. For array access the size of the subtypes is multiplied by the index and for members the offset of the member is calculated. Bug 20765 --- cparser/Elab.ml | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) (limited to 'cparser/Elab.ml') diff --git a/cparser/Elab.ml b/cparser/Elab.ml index 6256bf1f..68dd1b76 100644 --- a/cparser/Elab.ml +++ b/cparser/Elab.ml @@ -1646,14 +1646,39 @@ let elab_expr vararg loc env a = let (ty,env) = elab_type loc env spec dcl in if Cutil.incomplete_type env ty then error "offsetof of incomplete type %a" (print_typ env) ty; - let fld = match unroll env ty with - | TStruct(id,_) ->(wrap Env.find_struct_member loc env (id,mem)) - | TUnion (id,_)->(wrap Env.find_union_member loc env (id,mem)) - | _ -> - error "request offsetof for member '%s' in something not a structure" mem in - if List.exists (fun fld -> fld.fld_bitfield <> None) fld then - error "cannot compute the offset of bitfield '%s" mem; - let offset = Cutil.offsetof env ty fld in + let members env ty mem = + match ty with + | TStruct (id,_) -> wrap Env.find_struct_member loc env (id,mem) + | TUnion (id,_) -> wrap Env.find_union_member loc env (id,mem) + | _ -> error "request for member '%s' in something not a structure or union" mem in + let rec offset_of_list acc env ty = function + | [] -> acc,ty + | fld::rest -> let off = Cutil.offsetof env ty fld in + offset_of_list (acc+off) env fld.fld_typ rest in + let offset_of_member (env,off_accu,ty) mem = + match mem,unroll env ty with + | INFIELD_INIT mem,ty -> + let flds = members env ty mem in + let flds = List.rev flds in + let off,ty = offset_of_list 0 env ty flds in + env,off_accu + off,ty + | ATINDEX_INIT e,TArray (sub_ty,b,_) -> + let e,env = elab env e in + let e = + begin match Ceval.integer_expr env e,b with + | None,_ -> + error "array element designator for is not an integer constant expression" + | Some n,Some b -> if n >= b then + error "array index %Ld exceeds array bounds" n; + n + | Some n,None -> assert false + end in + let size = match sizeof env sub_ty with + | None -> assert false (* We expect only complete types *) + | Some s -> s in + env,off_accu + size * (Int64.to_int e),sub_ty + | ATINDEX_INIT _,_ -> error "subscripted value is not an array" in + let env,offset,_ = List.fold_left offset_of_member (env,0,ty) mem in let offsetof_const = EConst (CInt(Int64.of_int offset,size_t_ikind (),"")) in { edesc = offsetof_const; etyp = TInt(size_t_ikind(), []) },env -- cgit From 1fe7f0c7ec5e1a4425eb09939d0e8f6143f1f913 Mon Sep 17 00:00:00 2001 From: Bernhard Schommer Date: Tue, 31 Jan 2017 14:08:17 +0100 Subject: Remove superfluous check. Gcc and clang do not raise an error for this, also it should work for the last array element which can be without size. Bug 20765 --- cparser/Elab.ml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'cparser/Elab.ml') diff --git a/cparser/Elab.ml b/cparser/Elab.ml index 68dd1b76..f4f1586c 100644 --- a/cparser/Elab.ml +++ b/cparser/Elab.ml @@ -1662,16 +1662,13 @@ let elab_expr vararg loc env a = let flds = List.rev flds in let off,ty = offset_of_list 0 env ty flds in env,off_accu + off,ty - | ATINDEX_INIT e,TArray (sub_ty,b,_) -> + | ATINDEX_INIT e,TArray (sub_ty,_,_) -> let e,env = elab env e in let e = - begin match Ceval.integer_expr env e,b with - | None,_ -> + begin match Ceval.integer_expr env e with + | None -> error "array element designator for is not an integer constant expression" - | Some n,Some b -> if n >= b then - error "array index %Ld exceeds array bounds" n; - n - | Some n,None -> assert false + | Some n-> n end in let size = match sizeof env sub_ty with | None -> assert false (* We expect only complete types *) -- cgit From 3581ae495677aeca93f013d67a4d4f7c171d9cc0 Mon Sep 17 00:00:00 2001 From: Bernhard Schommer Date: Tue, 31 Jan 2017 14:21:50 +0100 Subject: Normalize offset to size_t kind. --- cparser/Elab.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'cparser/Elab.ml') diff --git a/cparser/Elab.ml b/cparser/Elab.ml index f4f1586c..3dc1816b 100644 --- a/cparser/Elab.ml +++ b/cparser/Elab.ml @@ -1676,8 +1676,10 @@ let elab_expr vararg loc env a = env,off_accu + size * (Int64.to_int e),sub_ty | ATINDEX_INIT _,_ -> error "subscripted value is not an array" in let env,offset,_ = List.fold_left offset_of_member (env,0,ty) mem in - let offsetof_const = EConst (CInt(Int64.of_int offset,size_t_ikind (),"")) in - { edesc = offsetof_const; etyp = TInt(size_t_ikind(), []) },env + let size_t = size_t_ikind () in + let offset = Ceval.normalize_int (Int64.of_int offset) size_t in + let offsetof_const = EConst (CInt(offset,size_t,"")) in + { edesc = offsetof_const; etyp = TInt(size_t, []) },env | UNARY(PLUS, a1) -> let b1,env = elab env a1 in -- cgit From ed55884ea9749f93ffd67f0734da0907fe338102 Mon Sep 17 00:00:00 2001 From: Bernhard Schommer Date: Tue, 31 Jan 2017 14:44:05 +0100 Subject: Avoid overflows and report an error. Instead of multiplying the array constant directly with the size of the offset the cautious_mul function is used to detect potential overflows. Bug 20765 --- cparser/Elab.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'cparser/Elab.ml') diff --git a/cparser/Elab.ml b/cparser/Elab.ml index 3dc1816b..61f51520 100644 --- a/cparser/Elab.ml +++ b/cparser/Elab.ml @@ -1664,16 +1664,16 @@ let elab_expr vararg loc env a = env,off_accu + off,ty | ATINDEX_INIT e,TArray (sub_ty,_,_) -> let e,env = elab env e in - let e = - begin match Ceval.integer_expr env e with - | None -> - error "array element designator for is not an integer constant expression" - | Some n-> n - end in + let e = match Ceval.integer_expr env e with + | None -> error "array element designator for is not an integer constant expression" + | Some n-> n in let size = match sizeof env sub_ty with | None -> assert false (* We expect only complete types *) | Some s -> s in - env,off_accu + size * (Int64.to_int e),sub_ty + let off_accu = match cautious_mul e size with + | None -> error "'offsetof' overflows" + | Some s -> off_accu + s in + env,off_accu,sub_ty | ATINDEX_INIT _,_ -> error "subscripted value is not an array" in let env,offset,_ = List.fold_left offset_of_member (env,0,ty) mem in let size_t = size_t_ikind () in -- cgit