diff --git a/gcc/calls.c b/gcc/calls.c
index b56556a..ecc9b7a 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -4835,7 +4835,10 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	 Note that in C the default argument promotions
 	 will prevent such mismatches.  */
 
-      size = GET_MODE_SIZE (arg->mode);
+      if (lang_hooks.decls.empty_record_p (TREE_TYPE (pval)))
+	size = 0;
+      else
+	size = GET_MODE_SIZE (arg->mode);
       /* Compute how much space the push instruction will push.
 	 On many machines, pushing a byte will advance the stack
 	 pointer by a halfword.  */
@@ -4865,10 +4868,14 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 
       /* This isn't already where we want it on the stack, so put it there.
 	 This can either be done with push or copy insns.  */
-      if (!emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
-		      parm_align, partial, reg, used - size, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), true))
+      if (used
+	  && !emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			      NULL_RTX, parm_align, partial, reg,
+			      used - size, argblock,
+			      ARGS_SIZE_RTX (arg->locate.offset),
+			      reg_parm_stack_space,
+			      ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			      true))
 	sibcall_failure = 1;
 
       /* Unless this is a partially-in-register argument, the argument is now
@@ -4900,10 +4907,16 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	{
 	  /* PUSH_ROUNDING has no effect on us, because emit_push_insn
 	     for BLKmode is careful to avoid it.  */
+	  bool empty_record
+	    = lang_hooks.decls.empty_record_p (TREE_TYPE (pval));
 	  excess = (arg->locate.size.constant
-		    - int_size_in_bytes (TREE_TYPE (pval))
+		    - (empty_record
+		       ? 0
+		       : int_size_in_bytes (TREE_TYPE (pval)))
 		    + partial);
-	  size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
+	  size_rtx = expand_expr ((empty_record
+				   ? size_zero_node
+				   : size_in_bytes (TREE_TYPE (pval))),
 				  NULL_RTX, TYPE_MODE (sizetype),
 				  EXPAND_NORMAL);
 	}
@@ -4971,10 +4984,13 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 	    }
 	}
 
-      emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
-		      parm_align, partial, reg, excess, argblock,
-		      ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
-		      ARGS_SIZE_RTX (arg->locate.alignment_pad), false);
+      if (!CONST_INT_P (size_rtx) || INTVAL (size_rtx) != 0)
+	emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval),
+			size_rtx, parm_align, partial, reg, excess,
+			argblock, ARGS_SIZE_RTX (arg->locate.offset),
+			reg_parm_stack_space,
+			ARGS_SIZE_RTX (arg->locate.alignment_pad),
+			false);
 
       /* Unless this is a partially-in-register argument, the argument is now
 	 in the stack.
@@ -5052,6 +5068,9 @@ must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type)
   if (TREE_ADDRESSABLE (type))
     return true;
 
+  if (lang_hooks.decls.empty_record_p (type))
+    return false;
+
   /* If the padding and mode of the type is such that a copy into
      a register would put it into the wrong part of the register.  */
   if (mode == BLKmode
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 6173dae..8bd6198 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -7920,6 +7920,9 @@ static int
 classify_argument (machine_mode mode, const_tree type,
 		   enum x86_64_reg_class classes[MAX_CLASSES], int bit_offset)
 {
+  if (type && lang_hooks.decls.empty_record_p (type))
+    return 0;
+
   HOST_WIDE_INT bytes =
     (mode == BLKmode) ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode);
   int words = CEIL (bytes + (bit_offset % 64) / 8, UNITS_PER_WORD);
@@ -8371,6 +8374,9 @@ construct_container (machine_mode mode, machine_mode orig_mode,
   static bool issued_sse_ret_error;
   static bool issued_x87_ret_error;
 
+  if (type && lang_hooks.decls.empty_record_p (type))
+    return NULL;
+
   machine_mode tmpmode;
   int bytes =
     (mode == BLKmode) ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode);
@@ -8783,6 +8789,9 @@ ix86_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
   HOST_WIDE_INT bytes, words;
   int nregs;
 
+  if (type && lang_hooks.decls.empty_record_p (type))
+    return;
+
   if (mode == BLKmode)
     bytes = int_size_in_bytes (type);
   else
@@ -9099,6 +9108,9 @@ ix86_function_arg (cumulative_args_t cum_v, machine_mode omode,
   HOST_WIDE_INT bytes, words;
   rtx arg;
 
+  if (type && lang_hooks.decls.empty_record_p (type))
+    return NULL;
+
   /* All pointer bounds arguments are handled separately here.  */
   if ((type && POINTER_BOUNDS_TYPE_P (type))
       || POINTER_BOUNDS_MODE_P (mode))
@@ -9708,6 +9720,9 @@ ix86_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
   if (POINTER_BOUNDS_TYPE_P (type))
     return false;
 
+  if (type && lang_hooks.decls.empty_record_p (type))
+    return false;
+
   if (TARGET_64BIT)
     {
       if (ix86_function_type_abi (fntype) == MS_ABI)
@@ -10266,7 +10281,8 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, false);
   if (indirect_p)
     type = build_pointer_type (type);
-  size = int_size_in_bytes (type);
+  bool empty_record = type && lang_hooks.decls.empty_record_p (type);
+  size = empty_record ? 0 : int_size_in_bytes (type);
   rsize = CEIL (size, UNITS_PER_WORD);
 
   nat_mode = type_natural_mode (type, NULL, false);
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 216a301..c380734 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -8068,8 +8068,8 @@ build_self_reference (void)
 
 /* Returns 1 if TYPE contains only padding bytes.  */
 
-int
-is_empty_class (tree type)
+bool
+is_empty_class (const_tree type)
 {
   if (type == error_mark_node)
     return 0;
@@ -8084,7 +8084,7 @@ is_empty_class (tree type)
    possible combinations of empty classes and possibly a vptr.  */
 
 bool
-is_really_empty_class (tree type)
+is_really_empty_class (const_tree type)
 {
   if (CLASS_TYPE_P (type))
     {
@@ -8098,10 +8098,13 @@ is_really_empty_class (tree type)
       if (COMPLETE_TYPE_P (type) && is_empty_class (type))
 	return true;
 
-      for (binfo = TYPE_BINFO (type), i = 0;
-	   BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
-	if (!is_really_empty_class (BINFO_TYPE (base_binfo)))
-	  return false;
+      /* TYPE_BINFO may be zeron when is_really_empty_class is called
+	 from LANG_HOOKS_EMPTY_RECORD_P.  */
+      binfo = TYPE_BINFO (type);
+      if (binfo)
+	for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
+	  if (!is_really_empty_class (BINFO_TYPE (base_binfo)))
+	    return false;
       for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
 	if (TREE_CODE (field) == FIELD_DECL
 	    && !DECL_ARTIFICIAL (field)
diff --git a/gcc/cp/cp-lang.c b/gcc/cp/cp-lang.c
index 048108d..80174cb 100644
--- a/gcc/cp/cp-lang.c
+++ b/gcc/cp/cp-lang.c
@@ -78,6 +78,8 @@ static tree cxx_enum_underlying_base_type (const_tree);
 #define LANG_HOOKS_EH_RUNTIME_TYPE build_eh_type_type
 #undef LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE
 #define LANG_HOOKS_ENUM_UNDERLYING_BASE_TYPE cxx_enum_underlying_base_type
+#undef LANG_HOOKS_EMPTY_RECORD_P
+#define LANG_HOOKS_EMPTY_RECORD_P is_really_empty_class
 
 /* Each front end provides its own lang hook initializer.  */
 struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 84437b4..dc79979 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5578,8 +5578,8 @@ extern tree finish_struct			(tree, tree);
 extern void finish_struct_1			(tree);
 extern int resolves_to_fixed_type_p		(tree, int *);
 extern void init_class_processing		(void);
-extern int is_empty_class			(tree);
-extern bool is_really_empty_class		(tree);
+extern bool is_empty_class			(const_tree);
+extern bool is_really_empty_class		(const_tree);
 extern void pushclass				(tree);
 extern void popclass				(void);
 extern void push_nested_class			(tree);
diff --git a/gcc/function.c b/gcc/function.c
index afc2c87..b59157d 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -4093,8 +4093,11 @@ locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs,
 
   part_size_in_regs = (reg_parm_stack_space == 0 ? partial : 0);
 
-  sizetree
-    = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
+  if (type)
+    sizetree = (lang_hooks.decls.empty_record_p (type)
+		? size_zero_node : size_in_bytes (type));
+  else
+    sizetree = size_int (GET_MODE_SIZE (passed_mode));
   where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
   boundary = targetm.calls.function_arg_boundary (passed_mode, type);
   round_boundary = targetm.calls.function_arg_round_boundary (passed_mode,
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index 18ac84d..df4cbf8 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -163,6 +163,7 @@ extern tree lhd_make_node (enum tree_code);
 #define LANG_HOOKS_GENERIC_GENERIC_PARAMETER_DECL_P hook_bool_const_tree_false
 #define LANG_HOOKS_FUNCTION_PARM_EXPANDED_FROM_PACK_P \
 					hook_bool_tree_tree_false
+#define LANG_HOOKS_EMPTY_RECORD_P	hook_bool_const_tree_false
 #define LANG_HOOKS_GET_GENERIC_FUNCTION_DECL hook_tree_const_tree_null
 #define LANG_HOOKS_TYPE_PROMOTES_TO lhd_type_promotes_to
 #define LANG_HOOKS_REGISTER_BUILTIN_TYPE lhd_register_builtin_type
@@ -228,6 +229,7 @@ extern tree lhd_make_node (enum tree_code);
   LANG_HOOKS_FUNCTION_DECL_DELETED_P, \
   LANG_HOOKS_GENERIC_GENERIC_PARAMETER_DECL_P, \
   LANG_HOOKS_FUNCTION_PARM_EXPANDED_FROM_PACK_P, \
+  LANG_HOOKS_EMPTY_RECORD_P, \
   LANG_HOOKS_GET_GENERIC_FUNCTION_DECL, \
   LANG_HOOKS_WARN_UNUSED_GLOBAL_DECL, \
   LANG_HOOKS_POST_COMPILATION_PARSING_CLEANUPS, \
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index d8d01fa..450bdee 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -177,6 +177,9 @@ struct lang_hooks_for_decls
      function parameter pack.  */
   bool (*function_parm_expanded_from_pack_p) (tree, tree);
 
+  /* Determine if a type is an empty record.  */
+  bool (*empty_record_p) (const_tree type);
+
   /* Returns the generic declaration of a generic function instantiations.  */
   tree (*get_generic_function_decl) (const_tree);
 
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index 5aae9e9..cc4cede 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -128,7 +128,7 @@ along with GCC; see the file COPYING3.  If not see
      String are represented in the table as pairs, a length in ULEB128
      form followed by the data for the string.  */
 
-#define LTO_major_version 5
+#define LTO_major_version 6
 #define LTO_minor_version 0
 
 typedef unsigned char	lto_decl_flags_t;
diff --git a/gcc/lto/lto-lang.c b/gcc/lto/lto-lang.c
index 53dd8f6..dc849a4 100644
--- a/gcc/lto/lto-lang.c
+++ b/gcc/lto/lto-lang.c
@@ -1306,6 +1306,16 @@ static void lto_init_ts (void)
   tree_contains_struct[NAMESPACE_DECL][TS_DECL_MINIMAL] = 1;
 }
 
+/* Return true if TYPE contains no actual data, just various possible
+   combinations of empty records.  */
+
+static bool
+lto_empty_record_p (const_tree type)
+{
+  /* Set if a record is empty.  */
+  return TYPE_LANG_FLAG_0 (type);
+}
+
 #undef LANG_HOOKS_NAME
 #define LANG_HOOKS_NAME "GNU GIMPLE"
 #undef LANG_HOOKS_OPTION_LANG_MASK
@@ -1363,6 +1373,9 @@ static void lto_init_ts (void)
 #undef LANG_HOOKS_INIT_TS
 #define LANG_HOOKS_INIT_TS lto_init_ts
 
+#undef LANG_HOOKS_EMPTY_RECORD_P
+#define LANG_HOOKS_EMPTY_RECORD_P lto_empty_record_p
+
 struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
 
 /* Language hooks that are not part of lang_hooks.  */
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index c34b4e9..5e74dd9 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -74,6 +74,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "opts.h"
 #include "gimplify.h"
+#include "langhooks.h"
 
 
 bool
@@ -1823,9 +1824,12 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
   /* Hoist the valist value into a temporary for the moment.  */
   valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL);
 
+  bool empty_record = lang_hooks.decls.empty_record_p (type);
+
   /* va_list pointer is aligned to PARM_BOUNDARY.  If argument actually
      requires greater alignment, we must perform dynamic alignment.  */
   if (boundary > align
+      && !empty_record
       && !integer_zerop (TYPE_SIZE (type)))
     {
       t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
@@ -1852,7 +1856,7 @@ std_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
     }
 
   /* Compute the rounded size of the type.  */
-  type_size = size_in_bytes (type);
+  type_size = empty_record ? size_zero_node : size_in_bytes (type);
   rounded_size = round_up (type_size, align);
 
   /* Reduce rounded_size so it's sharable with the postqueue.  */
diff --git a/gcc/testsuite/g++.dg/abi/empty12.C b/gcc/testsuite/g++.dg/abi/empty12.C
new file mode 100644
index 0000000..430d57d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty12.C
@@ -0,0 +1,17 @@
+// PR c++/60336
+// { dg-do run }
+// { dg-options "-x c" }
+// { dg-additional-sources "empty12a.c" }
+// { dg-prune-output "command line option" }
+
+#include "empty12.h"
+extern "C" void fun(struct dummy, struct foo);
+
+int main()
+{
+  struct dummy d;
+  struct foo f = { -1, -2, -3, -4, -5 };
+
+  fun(d, f);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/abi/empty12.h b/gcc/testsuite/g++.dg/abi/empty12.h
new file mode 100644
index 0000000..c61afcd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty12.h
@@ -0,0 +1,9 @@
+struct dummy { };
+struct foo
+{
+  int i1;
+  int i2;
+  int i3;
+  int i4;
+  int i5;
+};
diff --git a/gcc/testsuite/g++.dg/abi/empty12a.c b/gcc/testsuite/g++.dg/abi/empty12a.c
new file mode 100644
index 0000000..34a25ba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/empty12a.c
@@ -0,0 +1,6 @@
+#include "empty12.h"
+void fun(struct dummy d, struct foo f)
+{
+  if (f.i1 != -1)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/pr60336-1.C b/gcc/testsuite/g++.dg/pr60336-1.C
new file mode 100644
index 0000000..af08638
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-1.C
@@ -0,0 +1,17 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+struct dummy { };
+struct true_type { struct dummy i; };
+
+extern true_type y;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx9true_type" { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/testsuite/g++.dg/pr60336-2.C b/gcc/testsuite/g++.dg/pr60336-2.C
new file mode 100644
index 0000000..7b902e8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr60336-2.C
@@ -0,0 +1,28 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#include <stdarg.h>
+
+struct dummy { struct{}__attribute__((aligned (4))) a[7]; };
+
+void
+test (struct dummy a, ...)
+{
+  va_list va_arglist;
+  int i;
+
+  va_start (va_arglist, a);
+  i = va_arg (va_arglist, int);
+  if (i != 0x10)
+    __builtin_abort ();
+  va_end (va_arglist);
+}
+
+struct dummy a0;
+
+int
+main ()
+{
+  test (a0, 0x10);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/pr68355.C b/gcc/testsuite/g++.dg/pr68355.C
new file mode 100644
index 0000000..1354fc4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr68355.C
@@ -0,0 +1,24 @@
+// { dg-do compile }
+// { dg-options "-O2 -std=c++11 -fno-pic" }
+// { dg-require-effective-target fpic }
+
+template<typename _Tp, _Tp __v>
+struct integral_constant
+{
+  static constexpr _Tp value = __v;
+  typedef _Tp value_type;
+  typedef integral_constant<_Tp, __v> type;
+  constexpr operator value_type() const { return value; }
+};
+
+typedef integral_constant<bool, true> true_type;
+extern void xxx (true_type c);
+
+void
+yyy (void)
+{
+  true_type y;
+  xxx (y);
+}
+
+// { dg-final { scan-assembler "jmp\[\t \]+\[^\$\]*?_Z3xxx17integral_constantIbLb1EE" { target i?86-*-* x86_64-*-* } } }
diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c
index bb5cd49..6b1e25d 100644
--- a/gcc/tree-dfa.c
+++ b/gcc/tree-dfa.c
@@ -394,6 +394,8 @@ get_ref_base_and_extent (tree exp, HOST_WIDE_INT *poffset,
       machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
       if (mode == BLKmode)
 	size_tree = TYPE_SIZE (TREE_TYPE (exp));
+      else if (lang_hooks.decls.empty_record_p (TREE_TYPE (exp)))
+	bitsize = 0;
       else
 	bitsize = int (GET_MODE_PRECISION (mode));
     }
diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c
index 65a1ce3..1c32d81 100644
--- a/gcc/tree-streamer-in.c
+++ b/gcc/tree-streamer-in.c
@@ -154,6 +154,11 @@ unpack_ts_base_value_fields (struct bitpack_d *bp, tree expr)
     }
   else
     bp_unpack_value (bp, 9);
+  if (TYPE_P (expr))
+    /* Set if a record is empty.  */
+    TYPE_LANG_FLAG_0 (expr) = (unsigned) bp_unpack_value (bp, 1);
+  else
+     bp_unpack_value (bp, 1);
 }
 
 
diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c
index d0b7f6d..a1bf962 100644
--- a/gcc/tree-streamer-out.c
+++ b/gcc/tree-streamer-out.c
@@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "alias.h"
 #include "stor-layout.h"
 #include "gomp-constants.h"
+#include "langhooks.h"
 
 
 /* Output the STRING constant to the string
@@ -128,6 +129,11 @@ pack_ts_base_value_fields (struct bitpack_d *bp, tree expr)
     }
   else
     bp_pack_value (bp, 0, 9);
+  if (TYPE_P (expr))
+    /* Stream out a bit to indicate if a record is empty.  */
+    bp_pack_value (bp, lang_hooks.decls.empty_record_p (expr), 1);
+  else
+    bp_pack_value (bp, 0, 1);
 }
 
 
