diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 7b44126c745081c4c6da1ab827bae1b61f7c875a..d89c8923645436f2fad9631ac0079217a416b0b1 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,10 @@
+2004-12-28  Richard Henderson  <rth@redhat.com>
+
+	PR inline-asm/15740
+	* gimplify.c (gimplify_asm_expr): Move resolve asm names ...
+	* c-typeck.c (build_asm_expr): ... here.  Validate input
+	constraints.  Mark memory inputs addressable.
+
 2004-12-28  Hans-Peter Nilsson  <hp@bitrange.com>
 
 	PR target/18321
diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c
index d2963b374545bcce6e4ace76b67b38330cf936d3..5eae3aa7d5ec5edca018333d3b94488ff8623c9d 100644
--- a/gcc/c-typeck.c
+++ b/gcc/c-typeck.c
@@ -6287,47 +6287,74 @@ build_asm_expr (tree string, tree outputs, tree inputs, tree clobbers,
   tree args;
   int i;
   const char *constraint;
+  const char **oconstraints;
   bool allows_mem, allows_reg, is_inout;
-  int ninputs;
-  int noutputs;
+  int ninputs, noutputs;
 
   ninputs = list_length (inputs);
   noutputs = list_length (outputs);
+  oconstraints = (const char **) alloca (noutputs * sizeof (const char *));
+
+  string = resolve_asm_operand_names (string, outputs, inputs);
 
   /* Remove output conversions that change the type but not the mode.  */
   for (i = 0, tail = outputs; tail; ++i, tail = TREE_CHAIN (tail))
     {
       tree output = TREE_VALUE (tail);
+
+      /* ??? Really, this should not be here.  Users should be using a
+	 proper lvalue, dammit.  But there's a long history of using casts
+	 in the output operands.  In cases like longlong.h, this becomes a
+	 primitive form of typechecking -- if the cast can be removed, then
+	 the output operand had a type of the proper width; otherwise we'll
+	 get an error.  Gross, but ...  */
       STRIP_NOPS (output);
-      TREE_VALUE (tail) = output;
-      lvalue_or_else (output, lv_asm);
+
+      if (!lvalue_or_else (output, lv_asm))
+	output = error_mark_node;
 
       constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (tail)));
+      oconstraints[i] = constraint;
 
-      if (!parse_output_constraint (&constraint, i, ninputs, noutputs,
-                                    &allows_mem, &allows_reg, &is_inout))
-        {
-          /* By marking this operand as erroneous, we will not try
-          to process this operand again in expand_asm_operands.  */
-          TREE_VALUE (tail) = error_mark_node;
-          continue;
-        }
+      if (parse_output_constraint (&constraint, i, ninputs, noutputs,
+				   &allows_mem, &allows_reg, &is_inout))
+	{
+	  /* If the operand is going to end up in memory,
+	     mark it addressable.  */
+	  if (!allows_reg && !c_mark_addressable (output))
+	    output = error_mark_node;
+	}
+      else
+        output = error_mark_node;
 
-      /* If the operand is a DECL that is going to end up in
-        memory, assume it is addressable.  This is a bit more
-        conservative than it would ideally be; the exact test is
-        buried deep in expand_asm_operands and depends on the
-        DECL_RTL for the OPERAND -- which we don't have at this
-        point.  */
-      if (!allows_reg && DECL_P (output))
-        c_mark_addressable (output);
+      TREE_VALUE (tail) = output;
     }
 
   /* Perform default conversions on array and function inputs.
      Don't do this for other types as it would screw up operands
      expected to be in memory.  */
-  for (tail = inputs; tail; tail = TREE_CHAIN (tail))
-    TREE_VALUE (tail) = default_function_array_conversion (TREE_VALUE (tail));
+  for (i = 0, tail = inputs; tail; ++i, tail = TREE_CHAIN (tail))
+    {
+      tree input;
+
+      constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (tail)));
+      input = TREE_VALUE (tail);
+
+      input = default_function_array_conversion (input);
+
+      if (parse_input_constraint (&constraint, i, ninputs, noutputs, 0,
+				  oconstraints, &allows_mem, &allows_reg))
+	{
+	  /* If the operand is going to end up in memory,
+	     mark it addressable.  */
+	  if (!allows_reg && allows_mem && !c_mark_addressable (input))
+	    input = error_mark_node;
+	}
+      else
+	input = error_mark_node;
+
+      TREE_VALUE (tail) = input;
+    }
 
   args = build_stmt (ASM_EXPR, string, outputs, inputs, clobbers);
 
@@ -6337,6 +6364,7 @@ build_asm_expr (tree string, tree outputs, tree inputs, tree clobbers,
       ASM_VOLATILE_P (args) = 1;
       ASM_INPUT_P (args) = 1;
     }
+
   return args;
 }
 
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 622a0012a13ba1fdd9a2688084c8dac0608c2ba5..9e96120e8083bdea8c3ffc0ae699eb1832741ab3 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,9 @@
+2004-12-28  Richard Henderson  <rth@redhat.com>
+
+	PR inline-asm/15740
+	* semantics.c (finish_asm_stmt): Resolve asm names.  Validate input
+	constraints.  Mark memory inputs addressable.
+
 2004-12-27  Jason Merrill  <jason@redhat.com>
 
 	* decl.c (expand_static_init): Don't use shortcut if
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index ab1c028ff5d53885d82f2f0fe49f5ba674bf94cf..b34de199adb045bf66f99983bf79fbd229d2644a 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -1139,62 +1139,80 @@ finish_asm_stmt (int volatile_p, tree string, tree output_operands,
 
   if (!processing_template_decl)
     {
+      int ninputs, noutputs;
+      const char *constraint;
+      const char **oconstraints;
+      bool allows_mem, allows_reg, is_inout;
+      tree operand;
       int i;
-      int ninputs;
-      int noutputs;
 
-      for (t = input_operands; t; t = TREE_CHAIN (t))
+      ninputs = list_length (input_operands);
+      noutputs = list_length (output_operands);
+      oconstraints = (const char **) alloca (noutputs * sizeof (char *));
+
+      string = resolve_asm_operand_names (string, output_operands,
+					  input_operands);
+
+      for (i = 0, t = output_operands; t; t = TREE_CHAIN (t), ++i)
 	{
-	  tree converted_operand 
-	    = decay_conversion (TREE_VALUE (t)); 
-	  
+	  operand = TREE_VALUE (t);
+
+	  /* ??? Really, this should not be here.  Users should be using a
+	     proper lvalue, dammit.  But there's a long history of using
+	     casts in the output operands.  In cases like longlong.h, this
+	     becomes a primitive form of typechecking -- if the cast can be
+	     removed, then the output operand had a type of the proper width;
+	     otherwise we'll get an error.  Gross, but ...  */
+	  STRIP_NOPS (operand);
+
+	  if (!lvalue_or_else (operand, lv_asm))
+	    operand = error_mark_node;
+
+	  constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
+	  oconstraints[i] = constraint;
+
+	  if (parse_output_constraint (&constraint, i, ninputs, noutputs,
+				       &allows_mem, &allows_reg, &is_inout))
+	    {
+	      /* If the operand is going to end up in memory,
+		 mark it addressable.  */
+	      if (!allows_reg && !cxx_mark_addressable (operand))
+		operand = error_mark_node;
+	    }
+	  else
+	    operand = error_mark_node;
+
+	  TREE_VALUE (t) = operand;
+	}
+
+      for (i = 0, t = input_operands; t; ++i, t = TREE_CHAIN (t))
+	{
+	  constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
+	  operand = decay_conversion (TREE_VALUE (t)); 
+
 	  /* If the type of the operand hasn't been determined (e.g.,
 	     because it involves an overloaded function), then issue
 	     an error message.  There's no context available to
 	     resolve the overloading.  */
-	  if (TREE_TYPE (converted_operand) == unknown_type_node)
+	  if (TREE_TYPE (operand) == unknown_type_node)
 	    {
 	      error ("type of asm operand %qE could not be determined", 
                      TREE_VALUE (t));
-	      converted_operand = error_mark_node;
+	      operand = error_mark_node;
 	    }
-	  TREE_VALUE (t) = converted_operand;
-	}
-
-      ninputs = list_length (input_operands);
-      noutputs = list_length (output_operands);
-
-      for (i = 0, t = output_operands; t; t = TREE_CHAIN (t), ++i)
-	{
-	  bool allows_mem;
-	  bool allows_reg;
-	  bool is_inout;
-	  const char *constraint;
-	  tree operand;
 
-	  constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
-	  operand = TREE_VALUE (t);
-
-	  if (!parse_output_constraint (&constraint,
-					i, ninputs, noutputs,
-					&allows_mem,
-					&allows_reg,
-					&is_inout))
+	  if (parse_input_constraint (&constraint, i, ninputs, noutputs, 0,
+				      oconstraints, &allows_mem, &allows_reg))
 	    {
-	      /* By marking this operand as erroneous, we will not try
-		 to process this operand again in expand_asm_operands.  */
-	      TREE_VALUE (t) = error_mark_node;
-	      continue;
+	      /* If the operand is going to end up in memory,
+		 mark it addressable.  */
+	      if (!allows_reg && allows_mem && !cxx_mark_addressable (operand))
+		operand = error_mark_node;
 	    }
+	  else
+	    operand = error_mark_node;
 
-	  /* If the operand is a DECL that is going to end up in
-	     memory, assume it is addressable.  This is a bit more
-	     conservative than it would ideally be; the exact test is
-	     buried deep in expand_asm_operands and depends on the
-	     DECL_RTL for the OPERAND -- which we don't have at this
-	     point.  */
-	  if (!allows_reg && DECL_P (operand))
-	    cxx_mark_addressable (operand);
+	  TREE_VALUE (t) = operand;
 	}
     }
 
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index f89b7207c14836ff69d4e7757ec81e12fd4b8ad3..ca3759769b133331ef42cefa728e2495df7f7a98 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -3262,10 +3262,6 @@ gimplify_asm_expr (tree *expr_p, tree *pre_p, tree *post_p)
   bool allows_mem, allows_reg, is_inout;
   enum gimplify_status ret, tret;
 
-  ASM_STRING (expr)
-    = resolve_asm_operand_names (ASM_STRING (expr), ASM_OUTPUTS (expr),
-				 ASM_INPUTS (expr));
-
   ret = GS_ALL_DONE;
   for (i = 0, link = ASM_OUTPUTS (expr); link; ++i, link = TREE_CHAIN (link))
     {
diff --git a/gcc/testsuite/g++.dg/opt/asm2.C b/gcc/testsuite/g++.dg/opt/asm2.C
new file mode 100644
index 0000000000000000000000000000000000000000..4e3244169c779d9d83b42779251dddbfcd392274
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/asm2.C
@@ -0,0 +1,11 @@
+/* PR inline-asm/15740 */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+void foo(void)
+{
+  int a, b;
+  a = 1;
+  b = a + 1;
+  asm ("" : : "m" (a));
+}
diff --git a/gcc/testsuite/gcc.dg/asm-9.c b/gcc/testsuite/gcc.dg/asm-9.c
new file mode 100644
index 0000000000000000000000000000000000000000..4e3244169c779d9d83b42779251dddbfcd392274
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asm-9.c
@@ -0,0 +1,11 @@
+/* PR inline-asm/15740 */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+
+void foo(void)
+{
+  int a, b;
+  a = 1;
+  b = a + 1;
+  asm ("" : : "m" (a));
+}