473,830 Members | 2,055 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Volatiles in assignment expressions

Hi!

I tried to post this to comp.lang.c.mod erated but it didn't
seem to go through.

I've got a question about volatiles in assignment expressions.
I found the following code snippet in an embedded development
forum:

----
volatile char a;

...

if (++a == 1)
{
...
----

This code snippet was then followed by the output from two
compilers, one which was gcc. Gcc's code looked like something
like this (in RTL like form where Rn denotes a register):

R1 <- *a
R1 <- R1 + 1
*a <- R1
R2 <- *a
IF R2 == 1

Now to my question! The code above contains two read accesses
and one write access to a. I would have assumed that only one
read access should be performed. Does the standard allow the
second read operation?

I am adding a lot of standard texts that I've used when trying
to deduce the answer. Please help if you can!

Sorry for the long posting!

Regards
/Daniel W

P.s. Remove underscores from e-mail address if replying via
e-mail!

------------------------------------------------------------
Reading from the standard ISO/IEC [9899:1999] I find the
following passages related to this issue (A denotes the
rationale and B denotes the standard):

B.5.1.2.3 - 2 "Accessing a volatile object, modifying an object,
modifying a file, or calling a function that does any of those
operations are all side effects, which are changes in the state
of the execution environment. Evaluation of an expression may
produce side effects. At certain specified points in the execution
sequence called sequence points, all side effects of previous
evaluations shall be complete and no side effects of subsequent
evaluations shall have taken place." ...

B.6.5.16 - 3 "An assignment operator stores a value in the
object designated by the left operand. An assignment expression
has the value of the left operand after the assignment, but is
not an lvalue. The type of an assignment expression is the type
of the left operand unless the left operand has qualified type,
in which case it is the unqualified version of the type of the
left operand. The side effect of updating the stored value of
the left operand shall occur between the previous and the next
sequence point."

B.6.5.16.2 - 3 "A compound assignment of the form E1 op= E2
differs from the simple assignment expression E1 = E1 op E2 only
in that the lvalue E1 is evaluated only once."

B.6.3.2.1 - 2 "Except when it is the operand of the sizeof operator,
the unary & operator, the ++ operator, the -- operator, or the left
operand of the . operator or an assignment operator, an lvalue that
does not have array type is converted to the value stored in the
designated object (and is no longer an lvalue). If the lvalue have
qualified type, the value has the unqualified version of the type
of the lvalue; otherwise, the value has the type of the lvalue. If
the lvalue has an incomplete type and does not have array type, the
behavior is undefined."

B.6.5.3.1 - 2 "The value of the operand of the prefix ++ operator
is incremented. The result is the new value of the operand after
incrementation. The expression ++E is equivalent to (E+=1). See
discussions of additive operators and compound assignment for
information on constraints, types, side effects, and conversions
and the effects of operations on pointers."

B.6.7.3 - 3 "The properties associated with qualified types are
meaningfull only for expressions that are lvalues."

B.6.7.3 - 6 "An object that has volatile-qualified type may be
modified in ways unknown to the implementation or have other
unknown side effects. Therefore any expression refering to such
an object shall be evaulated strictly according to the rules of
the abstract machine, as described in 5.1.2.3. Furthermore, at
every sequence point the value stored in the object shall agree
with that prescribed by the abstract machine, except as modified
by the unknown factors mentioned previously. What constitutes
an access to an object that has volatile-qualified type is
implementetion-defined."

Annex C in the standard places a sequnce point after the controlling
expression of a selection statement.

A.6.5.2.4 "The C89 Committee did not endorse the practice in some
implementations of considering post-increment and post-decrement
operator expressions to be lvalues."

A.6.5.3.1 "See 6.5.2.4."

A.6.5.16 "The optimization rules for factoring out assignments can
also be stated. Let X(i,S) be an expression which contains no
impure functions or sequenced operators, and suppose that X
contains a storage S(i) to i which sets i to Snew(i) and returns
Sval(i). The possible expressions are

S(i): Sval(i): Snew(i):
++i i+1 i+1
i++ i i+1
--i i-1 i-1
i-- i i-1
y y y
i op= y i op y i op y

Then X(i,S) can be replaced by either

(T = i, i = Snew(i), X(T,Sval))

or

(T = X(i,Sval), i = Snew(i), T)

provided that neither i nor y have side effects themselves."
Mar 7 '06
13 1953

"Chris Torek" <no****@torek.n et> wrote in message
news:du*******@ news4.newsguy.c om...
On 2006-03-07, Daniel W <_R*********@ho tmail.com> wrote:

[given]
volatile char a;
...
if (++a == 1)

[gcc chooses to load, add, store, load again, and then compare,
i.e., re-read "a" after writing it in case the computed "old a
plus 1" is not still stored in "a".]

In article <sl************ ***********@ran dom.yi.org>
Jordan Abel <ra*******@gmai l.com> wrote:
That's because for some reason gcc chooses to read a again. However, the
C code only includes ONE read access - to read the original value which
is incremented.


This is not clear, or at least, not to me. (It happens to be what
I would *prefer*, but the model in the C standards is a bit vague.)
I believe the most important sentence, however, is this one from
6.5.3 of a C99 draft:

What constitutes an access to an object that has
volatile-qualified type is implementation-defined.

In other words, gcc can do anything at all here, provided what
it does is documented.


Hmm. The value of an object is the last value stored in it (ignoring
type-punning issues here). So, if we have that to hand, we can take the
value of the object without actually accessing the object, even in the
abstract machine.

But we're told that with a volatile object, the last store need not be
explicit in the program (C99 6.2.4 p2 footnote 26 -- the standard regards a
volatile object as one which does retain its last stored value, but which
may have values stored inexplicitly between one program access and the
next).

So, the abstract machine, if it needs to take the value of a volatile
object, cannot have the last stored value handy, and must fetch the value
from the object.

And with volatiles around, the real machine must forget about as-if rewrites
and do what the abstract machine does.

There's still the bit about "what constitutes an access ... is
implementation-defined". But if the latitude given to the implementor
allows him to define read access to a volatile object as "don't bother to
look at the object, just use the last value we wrote to it", then the whole
purpose of volatile is defeated and it's all nonsense. The scope given to
the implementor must be limited by the requirement to actually obtain the
last (inexplicitly) stored value.

(This means to say: I don't know what it is exactly that's
implementation-defined, but I can't see any room within the defined
semantics for it to make a difference)
When we come to the assignment expression, that unfortunate phrase "value of
the left operand" leads me to the conclusion that (with a volatile operand)
the read access (a la gcc) is not just optional but mandatory, on the
wording as it stands.
But there's a possible get-out in the tag "after the assignment". This has
two possible interpretations :

(A) immediately after the assignment, before anything else can happen
(B) after the assignment, and possibly after further writes to the object

Is it possible that the authors, if they'd been a bit more verbose, would
have written (B)? Hard to believe. The "further writes" can't be explicit
writes, the leeway could only extend to inexplicit writes to volatile
objects. What rationale could the committee have had for wanting to include
such a proviso, which basically would conflict with the normally understood
intention of assignments being expressions with values, as represented by
expressions like a = b = c = d?

On the other hand, (A) seems to conflict with footnote 26 mentioned above,
which apparently intends to say that the last stored value of a volatile
object can never be taken as known. But maybe not, on a closer argument.

We've been given the difficult example of a hardware register that takes a
command code and returns a status code. Since 6.2.4 p2 says that all C
objects retain their last-stored values, the only way we can reconcile this
beastie with C semantics is to say that the explicit write is immediately
followed by an inexplicit write: but since the two writes are separate
sequential events, there must logically be a conceptual interval during
which the last stored value of the C object (which is an abstraction, not
actually a piece of hardware) is indeed the value we just wrote,
notwithstanding that there is effectively no corresponding hardware state.
Of course we can't access this value by readback, but the C semantics don't
require that, since we know what it is by definition anyway.

With this argument, I claim that option (A) above is possible, i.e. not in
conflict with anything else, and obviously preferable to (B). Can I get
away with this?

--
RSH

Mar 8 '06 #11
Robin Haigh wrote:
"Chris Torek" <no****@torek.n et> wrote in message
news:du*******@ news4.newsguy.c om...
On 2006-03-07, Daniel W <_R*********@ho tmail.com> wrote:
[given]
volatile char a;
...
if (++a == 1)


[gcc chooses to load, add, store, load again, and then compare,
i.e., re-read "a" after writing it in case the computed "old a
plus 1" is not still stored in "a".]

In article <sl************ ***********@ran dom.yi.org>
Jordan Abel <ra*******@gmai l.com> wrote:
That's because for some reason gcc chooses to read a again. However, the
C code only includes ONE read access - to read the original value which
is incremented.


This is not clear, or at least, not to me. (It happens to be what
I would *prefer*, but the model in the C standards is a bit vague.)
I believe the most important sentence, however, is this one from
6.5.3 of a C99 draft:

What constitutes an access to an object that has
volatile-qualified type is implementation-defined.

In other words, gcc can do anything at all here, provided what
it does is documented.

Hmm. The value of an object is the last value stored in it (ignoring
type-punning issues here). So, if we have that to hand, we can take the
value of the object without actually accessing the object, even in the
abstract machine.


Yes, but the value of an assignment expression is not an alias
for the object assigned, i.e. the value of the assignment expression
should not be subject to any volatileness of the lvalue object? (C99
Rationale A.6.5.3.1).

The standard also states that it is the lvalue to rvalue conversion
that constitutes a read (B.6.3.2.1 p2), and if the value of an
assignment expression is not an lvalue, then no lvalue-to-rvalue
conversion can occur.
But we're told that with a volatile object, the last store need not be
explicit in the program (C99 6.2.4 p2 footnote 26 -- the standard regards a
volatile object as one which does retain its last stored value, but which
may have values stored inexplicitly between one program access and the
next).

So, the abstract machine, if it needs to take the value of a volatile
object, cannot have the last stored value handy, and must fetch the value
from the object.

And with volatiles around, the real machine must forget about as-if rewrites
and do what the abstract machine does.

There's still the bit about "what constitutes an access ... is
implementation-defined". But if the latitude given to the implementor
allows him to define read access to a volatile object as "don't bother to
look at the object, just use the last value we wrote to it", then the whole
purpose of volatile is defeated and it's all nonsense. The scope given to
the implementor must be limited by the requirement to actually obtain the
last (inexplicitly) stored value.

(This means to say: I don't know what it is exactly that's
implementation-defined, but I can't see any room within the defined
semantics for it to make a difference)

When we come to the assignment expression, that unfortunate phrase "value of
the left operand" leads me to the conclusion that (with a volatile operand)
the read access (a la gcc) is not just optional but mandatory, on the
wording as it stands.
Yet again, the C99 Rationale (A.6.5.16) gives only two possible
optimizations which, if taken as guideline on how things should
work in the non-optimized case, clearly indicates that the
intended functionality does not allow the second read. The only
possible optimization for the pre-increment case is:

(T = a + 1, a = T, T)

which do not contain the second read operation.
But there's a possible get-out in the tag "after the assignment". This has
two possible interpretations :

(A) immediately after the assignment, before anything else can happen
(B) after the assignment, and possibly after further writes to the object

Is it possible that the authors, if they'd been a bit more verbose, would
have written (B)? Hard to believe. The "further writes" can't be explicit
writes, the leeway could only extend to inexplicit writes to volatile
objects. What rationale could the committee have had for wanting to include
such a proviso, which basically would conflict with the normally understood
intention of assignments being expressions with values, as represented by
expressions like a = b = c = d?

On the other hand, (A) seems to conflict with footnote 26 mentioned above,
which apparently intends to say that the last stored value of a volatile
object can never be taken as known. But maybe not, on a closer argument.
It could also be that they wanted to say

(C) The value of an assignmend expression is the properly type
casted value assigned to the left operand
We've been given the difficult example of a hardware register that takes a
command code and returns a status code. Since 6.2.4 p2 says that all C
objects retain their last-stored values, the only way we can reconcile this
beastie with C semantics is to say that the explicit write is immediately
followed by an inexplicit write: but since the two writes are separate
sequential events, there must logically be a conceptual interval during
which the last stored value of the C object (which is an abstraction, not
actually a piece of hardware) is indeed the value we just wrote,
notwithstanding that there is effectively no corresponding hardware state.
Of course we can't access this value by readback, but the C semantics don't
require that, since we know what it is by definition anyway.
Again, I think that the rationale (A.6.5.16) gives a strong
hint that this is just the interpretation that they commitee
intended, i.e. the value *just* after passing it along the
data bus to the destination :-)
With this argument, I claim that option (A) above is possible, i.e. not in
conflict with anything else, and obviously preferable to (B). Can I get
away with this?


I feel inclined to say that (A) falls closer to what I would
like the standard to say. I would prefere even more if the
wording was more like (C).

/Daniel
(who likes his theories nice and concrete)
Mar 8 '06 #12

"Daniel W" <_R*********@ho tmail.com> wrote in message
news:44******** *************** @news3.bahnhof. se...
Robin Haigh wrote:
"Chris Torek" <no****@torek.n et> wrote in message
news:du*******@ news4.newsguy.c om...
On 2006-03-07, Daniel W <_R*********@ho tmail.com> wrote:

[given]

> volatile char a;
> ...
> if (++a == 1)

[gcc chooses to load, add, store, load again, and then compare,
i.e., re-read "a" after writing it in case the computed "old a
plus 1" is not still stored in "a".]

In article <sl************ ***********@ran dom.yi.org>
Jordan Abel <ra*******@gmai l.com> wrote:

That's because for some reason gcc chooses to read a again. However, theC code only includes ONE read access - to read the original value which
is incremented.

This is not clear, or at least, not to me. (It happens to be what
I would *prefer*, but the model in the C standards is a bit vague.)
I believe the most important sentence, however, is this one from
6.5.3 of a C99 draft:

What constitutes an access to an object that has
volatile-qualified type is implementation-defined.

In other words, gcc can do anything at all here, provided what
it does is documented.

Hmm. The value of an object is the last value stored in it (ignoring
type-punning issues here). So, if we have that to hand, we can take the
value of the object without actually accessing the object, even in the
abstract machine.


Yes, but the value of an assignment expression is not an alias
for the object assigned, i.e. the value of the assignment expression
should not be subject to any volatileness of the lvalue object? (C99
Rationale A.6.5.3.1).

The standard also states that it is the lvalue to rvalue conversion
that constitutes a read (B.6.3.2.1 p2), and if the value of an
assignment expression is not an lvalue, then no lvalue-to-rvalue
conversion can occur.
But we're told that with a volatile object, the last store need not be
explicit in the program (C99 6.2.4 p2 footnote 26 -- the standard regards a volatile object as one which does retain its last stored value, but which may have values stored inexplicitly between one program access and the
next).

So, the abstract machine, if it needs to take the value of a volatile
object, cannot have the last stored value handy, and must fetch the value from the object.

And with volatiles around, the real machine must forget about as-if rewrites and do what the abstract machine does.

There's still the bit about "what constitutes an access ... is
implementation-defined". But if the latitude given to the implementor
allows him to define read access to a volatile object as "don't bother to look at the object, just use the last value we wrote to it", then the whole purpose of volatile is defeated and it's all nonsense. The scope given to the implementor must be limited by the requirement to actually obtain the last (inexplicitly) stored value.

(This means to say: I don't know what it is exactly that's
implementation-defined, but I can't see any room within the defined
semantics for it to make a difference)

When we come to the assignment expression, that unfortunate phrase "value of the left operand" leads me to the conclusion that (with a volatile operand) the read access (a la gcc) is not just optional but mandatory, on the
wording as it stands.


Yet again, the C99 Rationale (A.6.5.16) gives only two possible
optimizations which, if taken as guideline on how things should
work in the non-optimized case, clearly indicates that the
intended functionality does not allow the second read. The only
possible optimization for the pre-increment case is:

(T = a + 1, a = T, T)

which do not contain the second read operation.
But there's a possible get-out in the tag "after the assignment". This has two possible interpretations :

(A) immediately after the assignment, before anything else can happen
(B) after the assignment, and possibly after further writes to the object
Is it possible that the authors, if they'd been a bit more verbose, would have written (B)? Hard to believe. The "further writes" can't be explicit writes, the leeway could only extend to inexplicit writes to volatile
objects. What rationale could the committee have had for wanting to include such a proviso, which basically would conflict with the normally understood intention of assignments being expressions with values, as represented by expressions like a = b = c = d?

On the other hand, (A) seems to conflict with footnote 26 mentioned above, which apparently intends to say that the last stored value of a volatile
object can never be taken as known. But maybe not, on a closer argument.
It could also be that they wanted to say

(C) The value of an assignmend expression is the properly type
casted value assigned to the left operand
We've been given the difficult example of a hardware register that takes

a command code and returns a status code. Since 6.2.4 p2 says that all C
objects retain their last-stored values, the only way we can reconcile this beastie with C semantics is to say that the explicit write is immediately followed by an inexplicit write: but since the two writes are separate
sequential events, there must logically be a conceptual interval during
which the last stored value of the C object (which is an abstraction, not actually a piece of hardware) is indeed the value we just wrote,
notwithstanding that there is effectively no corresponding hardware state. Of course we can't access this value by readback, but the C semantics don't require that, since we know what it is by definition anyway.


Again, I think that the rationale (A.6.5.16) gives a strong
hint that this is just the interpretation that they commitee
intended, i.e. the value *just* after passing it along the
data bus to the destination :-)
With this argument, I claim that option (A) above is possible, i.e. not in conflict with anything else, and obviously preferable to (B). Can I get
away with this?


I feel inclined to say that (A) falls closer to what I would
like the standard to say. I would prefere even more if the
wording was more like (C).


And there's another argument. Presumably it's not the intention to imply a
sequence point here. But if the wording is read as requiring two reads of a
modified object between sequence points, this is exactly analogous to a
situation that would normally produce undefined behaviour, because the
timing of the assignment is unspecified.

And if the ordering of the steps in the normal case is unspecified, then the
requirement that we must emulate the abstract machine in the presence of a
volatile object doesn't say much. As-if rewrites (where the abstract
semantics are specified, but an alternative is externally equivalent) are
banned, but any indeterminacy in the ordering of steps between sequence
points at the abstract machine level is still there.

--
RSH
Mar 8 '06 #13
Robin Haigh wrote:
<big snip>

And there's another argument. Presumably it's not the intention to imply a
sequence point here. But if the wording is read as requiring two reads of a
modified object between sequence points, this is exactly analogous to a
situation that would normally produce undefined behaviour, because the
timing of the assignment is unspecified.

And if the ordering of the steps in the normal case is unspecified, then the
requirement that we must emulate the abstract machine in the presence of a
volatile object doesn't say much. As-if rewrites (where the abstract
semantics are specified, but an alternative is externally equivalent) are
banned, but any indeterminacy in the ordering of steps between sequence
points at the abstract machine level is still there.


So what it boils down to is that we cannot use the value
of an assignment expression to a volatile object if we
want to guarantee the one-read-one-write scenario. Either
implementation (one read or two reads) are both within
the (fuzzy) limits of the standard.

So, if I want to be sure of the sequencing of, and number of
read operations in, the generated code, I must rely on a
temporary in the code, i.e.

t = a + 1;
a = t;
if (t /* or a */ == 1)
{
...

Many thanks
/Daniel

P.s. It's when I read texts like yours that I truly
comprehend the difference between beeing good at english
as a second language and beeing really good at english
as the primary language :-)
Mar 9 '06 #14

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

23
3245
by: Paul Rubin | last post by:
OK, I want to scan a file for lines matching a certain regexp. I'd like to use an assignment expression, like for line in file: if (g := re.match(pat, line)): croggle(g.group(1)) Since there are no assignment expressions in Python, I have to use a temp var. That's a little more messy, but bearable:
10
3327
by: Andrew Koenig | last post by:
It has been pointed out to me that various C++ books disagree about the relative precedence of ?: and the assignment operators. In order to satisfy myself about the matter once and for all, I looked at the grammar in the C++ standard. The relative fragments are as follows: conditional-expression: logical-or-expression logical-or-expression ? expression : assignment-expression
6
13388
by: Neil Zanella | last post by:
Hello, I would like to know whether the following C fragment is legal in standard C and behaves as intended under conforming implementations... union foo { char c; double d; };
77
4773
by: berns | last post by:
Hi All, A coworker and I have been debating the 'correct' expectation of evaluation for the phrase a = b = c. Two different versions of GCC ended up compiling this as b = c; a = b and the other ended up compiling it as a = c; b = c. (Both with no optimizations enabled). How did we notice you may ask? Well, in our case 'b' was a memory mapped register that only has a select number of writable bits. He claims it has been a 'C...
16
1365
by: Lauri Alanko | last post by:
The following code crashes on Solaris 10 when compiled without optimization: typedef struct Node Node; struct Node { int val; Node *next; };
20
2149
by: TimeHorse | last post by:
I would like to gauge interest in the following proposal: Problem: Assignment statements cannot be used as expressions. Performing a list of mutually exclusive checks that require data processing can cause excessive tabification. For example, consider the following python snipet...
9
3521
by: sturlamolden | last post by:
Python allows the binding behaviour to be defined for descriptors, using the __set__ and __get__ methods. I think it would be a major advantage if this could be generalized to any object, by allowing the assignment operator (=) to be overloaded. One particular use for this would be to implement "lazy evaluation". For example it would allow us to get rid of all the temporary arrays produced by NumPy. For example, consider the...
24
1642
by: boblatest | last post by:
Hello, I have an if-elif chain in which I'd like to match a string against several regular expressions. Also I'd like to use the match groups within the respective elif... block. The C-like idiom that I would like to use is this: if (match = my_re1.match(line): # use match elsif (match = my_re2.match(line)):
2
1507
by: donbock | last post by:
The paper Volatiles are Miscompiled, and What to Do about It by Eide and Regehr reports that mishandling of volatiles is a common category of compiler bugs. You should read it if you use volatiles for memory-mapped I/O or thread synchronization. Almost in passing they describe a common programmer error with volatiles: Inserting a "side-effecting operation" into the loop would also prevent the undesired transformation. Would inserting any...
0
9641
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
1
10520
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
10196
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
9310
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7739
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5615
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5775
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
4408
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
3956
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.