Re[9]: sequence point (по мотивам инкремента)
От: elcste  
Дата: 04.06.04 15:16
Оценка: 15 (1) :))) :)
Здравствуйте, jazzer, Вы писали:

J>Вы бы зарегистировались, что ли...

J>А то все Аноним до Аноним...

Ну что вы все пристали с этой регистрацией...
Re[10]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 10:28
Оценка: 14 (2)
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Теоретически потому что я не видел компилятора, использующего эту возможность.


Может быть кому-нибудь будет интересно посмотреть на код для процессора с явным пераллелизмом на уровне инструкцийй:
int i, j;

int main()
{
    return ++i + ++j;
}

Выдержка из получающегося ассемблерника для Itanium с моими комментариями

main:
{.mii
    add    r11=@gprel(j#),gp // загрузим смещения переменных i и j
    add    r10=@gprel(i#),gp
    nop.i   0 ;; // так обозначается та самая пресловутаю "точка следования" в asm :)
}
{.mmi
    ld4    r9=[r11]   // загрузим значения переменных
    ld4    r8=[r10]
    nop.i   0 ;;       // "точка следования"
}
{.mii
    add    r3=1,r9    // к каждому прибавим по 1 
    add    r2=1,r8 ;; // "точка следования", ждем завершения предыдущих операций
    add    r8=r2,r3   // складываем
}
{.mmi
    st4    [r11]=r3   // сохраняем новые значения переменных
    st4    [r10]=r2
    nop.i   0 ;;
}
{.mib
    nop.m   0                
    nop.i   0                
    br.ret.sptk.many b0 ;; // возвращаемся, в r8 - результат
}
Re[7]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 08:11
Оценка: +2
Здравствуйте, Bell, Вы писали:

А>>>>    f(i++) + f(i++);
А>>

А>>>>Понятно, почему здесь UB?

B>>>Здесь нет UB. Можно привести аргументацию?


А>>Можно, конечно. Здесь нет точек следования, разделяющих две модификации переменной i.

А>>Можете ли Вы такие точки следования указать?
B>Могу:

B>1.9/17
B>When calling a function (whether or not the function is inline), 
B>there is a sequence point after the evaluation of all function arguments (if any) 
B>which takes place before execution of any expressions or statements in 
B>the function body...

Наличие этих точек следования никто и не отрицал. Отсюда следует, что в каждом из подвыражений
    f(i++)
побочный эффект постинкремента произойдет до входа в функцию f(). Но отсюда не следует никаких ограничений на порядок произведения побочных эффектов двух постинкрементов переменной i. В соответствии с 5/4, неопределенное поведение.
Re[7]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 13:38
Оценка: 5 (1)
Здравствуйте, Lorenzo_LAMAS, Вы писали:

А>>(Должен сказать, что в этом вопросе я несколько радикальнее Павла. По моему мнению, например, в следующем примере — UB:

А>>
А>>    i = (++i, 0);
А>>

А>>Павел, если я все правильно помню, с этим согласен не был.)

L_L>А не могли бы Вы объяснить в чем тут, по Вашему мнению, дело? Т.е. Вы считаете, что все-таки между двумя точками следования i модифицируется дважды благодаря ++ и присваиванию и несмотря на точку следования после ',' ?


Да. Пронумеруем для наглядности точки следования.
    i = (++i, 0)  ;
  ^         ^    ^
  1         2    3
1 — где-то перед полным выражением; 2 — ее образует оператор запятая; 3 — в конце полного выражения.
У нас есть так же два побочных эффекта: присваивание и инкремент. Мы можем утверждать, что побочный эффект инкремента должен произойти между точками следования 1 и 2. Но о побочном эффекте присваивания мы можем сказать лишь, что он должен произойти между точками следования 1 и 3. Ни из чего в стандарте не следует, что побочный эффект присваивания должен происходить после всех побочных эффектов подвыражения в правой части.

Иначе. Этот пример можно рассматривать следующим образом.
    i = A;
где подвыражение A модифицирует переменную i. Точек следования в полном выражении нет. Переменная i модифицируется более одного раза между двумя соседними точками следования (1 и 3).

P.S. В этом документе дается более подробное и лучше формализованное, чем в стандартах C/C++, описание точек следования. (В нем, в частности, рассматриваемый пример имеет well-defined behavior.) Но документ этот так и не стал нормативным ни для C, ни для C++.
Re[2]: sequence point (по мотивам инкремента)
От: Павел Кузнецов  
Дата: 04.06.04 08:52
Оценка: 3 (1)
> А>
> А>    std::cout << f(i) << " " << f(i++) << " " << f(++i) << " " << f(i++) << std::endl;
> А>

>
> А>Или здесь UB? Почему?
>
> В таком случае стандарт не гарантирует именно ту последовательность вызовов функции f(), в которой ты написал. Порядок вызовов произволен.

Главное не в порядке вызовов функции, а в порядке вычисления аргументов — а вот он-то и не задан. Т.е. скалярный объект i может модифицироваться без наличия между модификациями точки следования — неопределенное поведение.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 07:07
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А>Из стандарта, я понял, что вызов функции является sequence point.


Двумя.

А>Если это так, объясните мне, как работает следующий фрагмент кода:


Следующий фрагмент кода не работает.

А>#include <iostream>

А>int f(int i) { return i;}

А>void main()
А>{
А>    int i = 0;
А>    std::cout << f(i) << " " << f(i++) << " " << f(++i) << " " << f(i++) << std::endl;
А>}

А>Или здесь UB?

Да.

А>Почему?


Потому, что переменная i модифицируется более одного раза между двумя соседними точками следования.
Re[3]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 07:25
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А>>>Из стандарта, я понял, что вызов функции является sequence point.

А>>Двумя.
А>то есть перед началом вызова и после вызова?

После вычисления аргументов и после копирования возвращаемого значения.

А>>>Если это так, объясните мне, как работает следующий фрагмент кода:

А>>Следующий фрагмент кода не работает.
А>он работает даже если тут UB, просто на всех компиляторах по-разному.

В том числе компилятор может, например, прервать трансляцию с выдачей диагностического сообщения.

А>И ИМХО нет тут никакого UB.

А>operator<< принимает один аргумент код эквивалентен серии вложенных вызовов operator<<
А>откуда здесь возмётся UB?

Уже было сказано, откуда:

А>>Потому, что переменная i модифицируется более одного раза между двумя соседними точками следования.


Давайте по порядку.

    f(i++) + f(i++);

Понятно, почему здесь UB?
Re[5]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 07:33
Оценка: +1
Здравствуйте, Bell, Вы писали:

А>>    f(i++) + f(i++);

А>>Понятно, почему здесь UB?

B>Здесь нет UB. Можно привести аргументацию?


Можно, конечно. Здесь нет точек следования, разделяющих две модификации переменной i.
Можете ли Вы такие точки следования указать?
sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 06:15
Оценка:
Из стандарта, я понял, что вызов функции является sequence point. Если это так, объясните мне, как работает следующий фрагмент кода:

#include <iostream>

int f(int i) { return i;}

void main()
{
    int i = 0;
    std::cout << f(i) << " " << f(i++) << " " << f(++i) << " " << f(i++) << std::endl;
}


Или здесь UB? Почему?
Re[2]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 07:13
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Аноним, Вы писали:


А>>Из стандарта, я понял, что вызов функции является sequence point.


А>Двумя.


то есть перед началом вызова и после вызова?

А>>Если это так, объясните мне, как работает следующий фрагмент кода:


А>Следующий фрагмент кода не работает.


он работает даже если тут UB, просто на всех компиляторах по-разному. И ИМХО нет тут никакого UB.
operator<< принимает один аргумент код эквивалентен серии вложенных вызовов operator<<
откуда здесь возмётся UB?

А>
А>>#include <iostream>

А>>int f(int i) { return i;}

А>>void main()
А>>{
А>>    int i = 0;
А>>    std::cout << f(i) << " " << f(i++) << " " << f(++i) << " " << f(i++) << std::endl;
А>>}
А>

А>>Или здесь UB?

А>Да.


А>>Почему?


А>Потому, что переменная i модифицируется более одного раза между двумя соседними точками следования.
Re: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 07:18
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Из стандарта, я понял, что вызов функции является sequence point. Если это так, объясните мне, как работает следующий фрагмент кода:


А>
А>#include <iostream>

А>int f(int i) { return i;}

А>void main()
А>{
А>    int i = 0;
А>    std::cout << f(i) << " " << f(i++) << " " << f(++i) << " " << f(i++) << std::endl;
А>}

А>


А>Или здесь UB? Почему?


В начале более простой чем у тебя пример "(a++, b++) + (c++, d++)". Здесь Стандарт гарантирует следующее a++ произойдет раньеше чем b++, и c++ произойдет раньше чем d++, но он не говорит, что левый операнд будет вычислен до правого или на оборот. Более того реализация может смешивать вычисления левого и правого операнда. Точки следования в операторе запятая нет, но есть гарантированный порядок вычисления.

Точки следования дают только относительную детерминированность. Так что в твоем примере, гарантируется, что увеличение i произойдет до вызова функции и будет сохранено в память, но в каком порядке будут вызваны функции или правильно ли произойдет увеличении не гарантируется. Так например, реализация может в начале сделать все увеличения, потом позвать все функции, а потом все напечатать. Так что вызовами функций, ты не введешь определенность порядка вычислений операндов. Кстати сказать вызов перегружженорго опратора << — это тоже точка следования, так что можно было и не вызывать функции.
Re: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 07:20
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Из стандарта, я понял, что вызов функции является sequence point. Если это так, объясните мне, как работает следующий фрагмент кода:


А>
А>#include <iostream>

А>int f(int i) { return i;}

А>void main()
А>{
А>    int i = 0;
А>    std::cout << f(i) << " " << f(i++) << " " << f(++i) << " " << f(i++) << std::endl;
А>}

А>


А>Или здесь UB? Почему?


В таком случае стандарт не гарантирует именно ту последовательность вызовов функции f(), в которой ты написал. Порядок вызовов произволен.
Re[3]: sequence point (по мотивам инкремента)
От: Bell Россия  
Дата: 04.06.04 07:25
Оценка:
Здравствуйте, Аноним, Вы писали:

А>он работает даже если тут UB, просто на всех компиляторах по-разному. И ИМХО нет тут никакого UB.

А>operator<< принимает один аргумент код эквивалентен серии вложенных вызовов operator<<
А>откуда здесь возмётся UB?

Здесь действительно нет UB. Пробрема просто в том, что порядок вычисления аргументов функции не определен.
Любите книгу — источник знаний (с) М.Горький
Re[4]: sequence point (по мотивам инкремента)
От: Bell Россия  
Дата: 04.06.04 07:28
Оценка:
Здравствуйте, Аноним, Вы писали:


А>Давайте по порядку.


А>
А>    f(i++) + f(i++);
А>

А>Понятно, почему здесь UB?

Здесь нет UB. Можно привести аргументацию?
Любите книгу — источник знаний (с) М.Горький
Re[2]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 07:28
Оценка:
Здравствуйте, dupamid, Вы писали:

D>Точки следования в операторе запятая нет, но есть гарантированный порядок вычисления.


Вот это новость!..
Re[4]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 07:30
Оценка:
Здравствуйте, Bell, Вы писали:

B>Здесь действительно нет UB.


Т.е. здесь нет нарушения требования из 5/4?
Re[5]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 07:37
Оценка:
Здравствуйте, Bell, Вы писали:

B>Здравствуйте, Аноним, Вы писали:



А>>Давайте по порядку.


А>>
А>>    f(i++) + f(i++);
А>>

А>>Понятно, почему здесь UB?

B>Здесь нет UB. Можно привести аргументацию?

Запишем так:
operator+(f(i++),f(i++))

Здесь есть UB только если функция f производит какие-то побочные эффекты
Смысл в том, что компилятор не гарантирует в каком порядке он вычисляет аргументы функции
operator+. operator+ — бинарный оператор, у которого аргументы одного типа. то же самое нельзя сказать
про оператор << в данном контексте. поэтому сравнение некорректно.
Re[4]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 07:39
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Давайте по порядку.

А>
А>    f(i++) + f(i++);
А>

А>Понятно, почему здесь UB?

Здесь нет undefined behavior, но результат неопределен — это разные вещи. Более того, несмотря на то что Павел Кузнецов утверждает, что модификация одной переменной между точками следования — есть undefined behavior, пока это не отднозначно. Но то что это как минимум unspecified behavior — это точно.
Re[6]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 07:39
Оценка:
А>Здесь есть UB только если функция f производит какие-то побочные эффекты
пардон эту строчку считать вычеркнутой здесь есть UB см. пост выше почему и почему это UB
нельзя так просто перенести на случай operator<<
Re[3]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 07:46
Оценка:
Здравствуйте, Аноним, Вы писали:

D>>Точки следования в операторе запятая нет, но есть гарантированный порядок вычисления.


А>Вот это новость!..


По С99 есть, вот цитата из С99:
6.5.17 Comma operator
Syntax
1 expression:
assignment-expression
expression , assignment-expression
Semantics
2 The left operand of a comma operator is evaluated as a void expression; there is a sequence point after its evaluation. Then the right operand is evaluated; the result has its type and value.94) If an attempt is made to modify the result of a comma operator or to access it after the next sequence point, the behavior is undefined.
3 EXAMPLE As indicated by the syntax, the comma operator (as described in this subclause) cannot appear in contexts where a comma is used to separate items in a list (such as arguments to functions or lists of initializers). On the other hand, it can be used within a parenthesized expression or within the second expression of a conditional operator in such contexts. In the function call f(a, (t=3, t+2), c) the function has three arguments, the second of which has the value 5.
Forward references: initialization (6.7.8).


По С++'04 нет:
5.18 Comma operator
1 The comma operator groups left-to-right.
expression:
assignment-expression
expression , assignment-expression
A pair of expressions separated by a comma is evaluated left-to-right and the value of the left expression is discarded. The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are not applied to the left expression. All side effects (1.9) of the left expression, except for the destruction of temporaries (12.2), are performed before the evaluation of the right expression. The type and value of the result are the type and value of the right operand; the result is an lvalue if the right operand is an lvalue, and is a bit-field if the right operand is an lvalue and a bit-field.
2 In contexts where comma is given a special meaning, [Example: in lists of arguments to functions (5.2.2) and lists of initializers (8.5) ] the comma operator as described in clause 5 can appear only in parentheses. [Example:
f(a, (t=3, t+2), c);
has three arguments, the second of which has the value 5. ]
Re[3]: sequence point (по мотивам инкремента)
От: jazzer Россия Skype: enerjazzer
Дата: 04.06.04 07:50
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, dupamid, Вы писали:


D>>Точки следования в операторе запятая нет, но есть гарантированный порядок вычисления.


А>Вот это новость!..


тем не менее, это так.
точка следования — это когда все побочные эффекты предыдущего выражения уже сработали, а следующего — еще не начались.
Для оператора запятая гарантируется только первое.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[5]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 07:53
Оценка:
Здравствуйте, dupamid, Вы писали:

А>>Давайте по порядку.

А>>
А>>    f(i++) + f(i++);
А>>

А>>Понятно, почему здесь UB?

D>Здесь нет undefined behavior, но результат неопределен — это разные вещи.


Нет, здесь не unspecified, а именно undefined behavior.

D>Более того, несмотря на то что Павел Кузницов утверждает,


(Должен сказать, что в этом вопросе я несколько радикальнее Павла. По моему мнению, например, в следующем примере — UB:
    i = (++i, 0);

Павел, если я все правильно помню, с этим согласен не был.)

D>что модификация одной переменной между точками следования — есть undefined behavior, пока это не отднозначно. Но то что это как минимум unspecified behavior — это точно.


Когда перестал действовать параграф 5/4 стандарта?
Re[6]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 07:55
Оценка:
Здравствуйте, Аноним, Вы писали:

А>
А>>>    f(i++) + f(i++);
А>

А>>>Понятно, почему здесь UB?

B>>Здесь нет UB. Можно привести аргументацию?


А>Можно, конечно. Здесь нет точек следования, разделяющих две модификации переменной i.

А>Можете ли Вы такие точки следования указать?

А точки перед вызовом функции и прямо перед возвратом из нее. Я считаю, компилятор, если у него одной переменной дважды между точками следовования приводит к каким-то проблемам, не должен оптимизировать это выражение таким образом. То, что результат не определено — это вопросов не вызывает.
Re[6]: sequence point (по мотивам инкремента)
От: Bell Россия  
Дата: 04.06.04 07:56
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Bell, Вы писали:


А>
А>>>    f(i++) + f(i++);
А>

А>>>Понятно, почему здесь UB?

B>>Здесь нет UB. Можно привести аргументацию?


А>Можно, конечно. Здесь нет точек следования, разделяющих две модификации переменной i.

А>Можете ли Вы такие точки следования указать?
Могу:

1.9/17
When calling a function (whether or not the function is inline), 
there is a sequence point after the evaluation of all function arguments (if any) 
which takes place before execution of any expressions or statements in 
the function body...


Другое деле, что порядок вычисления этих аргументов не определен, но это не UB, а "unspecified behavior".
Любите книгу — источник знаний (с) М.Горький
Re[4]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 08:01
Оценка:
Здравствуйте, dupamid, Вы писали:

D>>>Точки следования в операторе запятая нет, но есть гарантированный порядок вычисления.


А>>Вот это новость!..


D>По С++'04 нет:


Просветите, пожалуйста, что такое С++'04. Уже принят TC2?

D>[i]5.18 Comma operator


А какова теперь редакция 1.9/18?
Re[7]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 08:01
Оценка:
Здравствуйте, Bell, Вы писали:

B>Другое деле, что порядок вычисления этих аргументов не определен, но это не UB, а "unspecified behavior".


[offtopic]
UB — плохой термин, так как и "unspecified behavior" и "undefined behavior" сокращаются одинаково
[/offtopic]
Re[8]: sequence point (по мотивам инкремента)
От: Bell Россия  
Дата: 04.06.04 08:07
Оценка:
Здравствуйте, dupamid, Вы писали:

D>[offtopic]

D>UB — плохой термин, так как и "unspecified behavior" и "undefined behavior" сокращаются одинаково
D>[/offtopic]

Да, конечно.
Любите книгу — источник знаний (с) М.Горький
Re[7]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 08:13
Оценка:
Здравствуйте, dupamid, Вы писали:

D>Я считаю, компилятор, если у него одной переменной дважды между точками следовования приводит к каким-то проблемам, не должен оптимизировать это выражение таким образом.


Но где это требование в стандарте?
Re[5]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 08:14
Оценка:
Здравствуйте, Аноним, Вы писали:

D>>>>Точки следования в операторе запятая нет, но есть гарантированный порядок вычисления.


А>>>Вот это новость!..


D>>По С++'04 нет:


А>Просветите, пожалуйста, что такое С++'04. Уже принят TC2?


Это рабочий драфт Стандарта на 15 январа 2004 (есть апрельский, но его надо искать). Но ни в официально версии стандарта С++'03 ни в драфте это место не менялось с С++'89.

D>>5.18 Comma operator


А>А какова теперь редакция 1.9/18?


Редакция осталась без изменений:
1.9\18 In the evaluation of each of the expressions
a && b
a || b
a ? b : c
a , b
using the built-in meaning of the operators in these expressions (5.14, 5.15, 5.16, 5.18), there is a sequence point after the evaluation of the first expression 12).

12) The operators indicated in this paragraph are the built-in operators, as described in clause 5. When one of these operators is overloaded (clause 13) in a valid context, thus designating a user-defined operator function, the expression designates a function invocation, and the operands form an argument list, without an implied sequence point between them.


Это слегка противоречит тому, что написано в 5.18...
Re[8]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 08:22
Оценка:
Здравствуйте, Аноним, Вы писали:

A>побочный эффект постинкремента произойдет до входа в функцию f(). Но отсюда не следует никаких ограничений на порядок произведения побочных эффектов двух постинкрементов переменной i. В соответствии с 5/4, неопределенное поведение.


Ну тогда и тут можно увидеть неопределенное поведение: "i++; i++;" или "i++, i++".
Re[9]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 08:23
Оценка:
D>Ну тогда и тут можно увидеть неопределенное поведение: "i++; i++;" или "i++, i++".

Это конечно шутка — здесь все определено!
Re[6]: sequence point (по мотивам инкремента)
От: itman itman.livejournal.com
Дата: 04.06.04 08:23
Оценка:
Господа, вы в дискуссии о разнице между UB и UB!!! отошли от самого главного, а именно от того, определен ли
порядок вызовов функции f в коде
cout<<f(i++)<<f(i++).
мне кажется, что этот код эквивалентен
( cout.operator <<(f(i++)) ).operator <<(f(i++))
Второй вызов функции f может произойти только после того, как создастся временный объект. Где здесь может возникнуть неопределенность?
В свою очередь инкремент переменной i будет происходить дважды и опять-таки после вывода f(i) в поток вывода. пардон за каламбур, но пред очередным вызовом функции.
Если я что-то неправильно написал, просьба меня поправить
Внесены исправления автора — ПК
Re[5]: sequence point (по мотивам инкремента)
От: Павел Кузнецов  
Дата: 04.06.04 08:25
Оценка:
> несмотря на то что Павел Кузнецов утверждает, что модификация одной переменной между точками следования — есть undefined behavior, пока это не отднозначно

Давненько эта тема не поднималась... Если я правильно помню, твое мнение базировалось на наличии дефект-репорта 351. За это время комитетчики успели по нему формально проголосовать, как и ожидалось, подтвердив нормативную часть: http://open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#351 Итого, уже без каких-либо сомнений — undefined
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[6]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 08:27
Оценка:
Здравствуйте, dupamid, Вы писали:

D>Это рабочий драфт Стандарта на 15 январа 2004 (есть апрельский, но его надо искать). Но ни в официально версии стандарта С++'03 ни в драфте это место не менялось с С++'89.


В Вашей цитате было одно отличие в параграфе 1: "and is a bit-field if the right operand is an lvalue and a bit-field." Этого дополнения раньше не было.

D>Это слегка противоречит тому, что написано в 5.18...


В чем же Вы видите противоречие?
Re[7]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 08:31
Оценка:
Здравствуйте, itman, Вы писали:

I>Господа, вы в дискуссии о разнице между UB и UB!!! отошли от самого главного, а именно от того, определен ли

I>порядок вызовов функции f в коде
I>cout<<f(i++)<<f(i++).
I>мне кажется, что этот код эквивалентен
I>( cout.operator cout(f(i++)) ).operator cout(f(i++))

А это что за странный оператор "operator cout"

I>Второй вызов функции f может произойти только после того, как создастся временный объект. Где здесь может возникнуть неопределенность?


Какой временный объект? Выражение на самом деле такое "operator <<(operator <<(cout, f(i++)), f(i++))" причем оператор << возвращает не временный объект, а ссылку на ostraem.

I>В свою очередь инкремент переменной i будет происходить дважды и опять-таки после вывода f(i) в поток вывода. пардон за каламбур, но пред очередным вызовом функции.


Дело в том, что аргументы могут вычисляться раньше вызовова функции, что здесь и происходит, реализация вначале вычисляет все аргументы, которые может быз вызовов функций, а потом вызывает функции, и вычисляет оставшиеся.
Re: sequence point (по мотивам инкремента)
От: Кодт Россия  
Дата: 04.06.04 08:35
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Из стандарта, я понял, что вызов функции является sequence point. Если это так, объясните мне, как работает следующий фрагмент кода:


А>
А>#include <iostream>

А>int f(int i) { return i;}

А>void main()
А>{
А>    int i = 0;
А>    std::cout << f(i) << " " << f(i++) << " " << f(++i) << " " << f(i++) << std::endl;
А>}

А>


А>Или здесь UB? Почему?


Вызов функции является sequence point. А вот вычисление аргумента для функции — нет
Поэтому undefined.

Если бы ты сделал автоинкремент изолированным:
int predinc(int& i) { return ++i; }
int postinc(int& i) { return i++; }

....
А>    std::cout << f(i) << " " << f(postinc(i)) << " " << f(predinc(i)) << " " << f(postinc(i)) << std::endl;
....

то получил бы менее тяжкое, unspecified behaviour.
Перекуём баги на фичи!
Re[7]: sequence point (по мотивам инкремента)
От: Павел Кузнецов  
Дата: 04.06.04 08:37
Оценка:
> определен ли порядок вызовов функции f в коде
> cout<<f(i++)<<f(i++).
> мне кажется, что этот код эквивалентен
> ( cout.operator <<(f(i++)) ).operator <<(f(i++))
> Второй вызов функции f может произойти только после того, как создастся временный объект. Где здесь может возникнуть неопределенность?

Вне зависимости от порядка вызовов функции, ее аргументы могут вычисляться в разной последовательности, а — теоретически — даже и одновременно. Т.е. компилятор может сначала вычислить аргументы всех вызовов в одном выражении, а уж потом вызывать функции. Т.е. как минимум в одной возможной последовательности вычисления выражения, между двумя модификациями одного скалярного объекта нет точки следования. Согласно стандарту это приводит к неопределенному поведению.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[4]: sequence point (по мотивам инкремента)
От: Павел Кузнецов  
Дата: 04.06.04 08:40
Оценка:
> точка следования — это когда все побочные эффекты предыдущего выражения уже сработали, а следующего — еще не начались.
> Для оператора запятая гарантируется только первое.

И второе тоже: побочные эффекты выражения не могут начаться раньше его вычисления
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[8]: sequence point (по мотивам инкремента)
От: itman itman.livejournal.com
Дата: 04.06.04 08:41
Оценка:
D>А это что за странный оператор "operator cout"

см. пост выше я исправил

I>>Второй вызов функции f может произойти только после того, как создастся временный объект. Где здесь может возникнуть неопределенность?


D>Какой временный объект? Выражение на самом деле такое "operator <<(operator <<(cout, f(i++)), f(i++))" причем оператор << возвращает не временный объект, а ссылку на ostraem.


ага, ну я понял свою ошибку, спасибо. да так действительно UB или UB, поскольку порядок вычисления аргументов не гарантирован.

I>>В свою очередь инкремент переменной i будет происходить дважды и опять-таки после вывода f(i) в поток вывода. пардон за каламбур, но пред очередным вызовом функции.


D>Дело в том, что аргументы могут вычисляться раньше вызовова функции, что здесь и происходит, реализация вначале вычисляет все аргументы, которые может быз вызовов функций, а потом вызывает функции, и вычисляет оставшиеся.
Re[8]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 08:49
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Вне зависимости от порядка вызовов функции, ее аргументы могут вычисляться в разной последовательности, а — теоретически — даже и одновременно.


Почему же теортетически: на архитектурах с явным параллелизмом на уровне инструкций (например Itanium) так и есть.

ПК> Т.е. компилятор может сначала вычислить аргументы всех вызовов в одном выражении, а уж потом вызывать функции. Т.е. как минимум в одной возможной последовательности вычисления выражения, между двумя модификациями одного скалярного объекта нет точки следования. Согласно стандарту это приводит к неопределенному поведению.


Собственно вопрос именно в этом и есть: где написано, что если есть хотя одна такая последовательность, то компилятор может выбрать именно ее. В случае когда есть последовательности с точками следования, в которых таких проблем нет. Т.е. что компилятор может выносить вычисления за точки следования. Я считаю, что он может все перемещать только в пределех между двумя точками следования, но ни в коем случае за их пределеами. Т.е. точки следования ставят барьеры в пределеах которых, можно тасовать вычисления как компилятор захочет, пока не нарушаются зависимости по данным.
Re[5]: sequence point (по мотивам инкремента)
От: jazzer Россия Skype: enerjazzer
Дата: 04.06.04 08:58
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

>> точка следования — это когда все побочные эффекты предыдущего выражения уже сработали, а следующего — еще не начались.

>> Для оператора запятая гарантируется только первое.

ПК>И второе тоже: побочные эффекты выражения не могут начаться раньше его вычисления :-)


А есть ли в Стандарте такое требование? ;)
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[9]: sequence point (по мотивам инкремента)
От: Павел Кузнецов  
Дата: 04.06.04 09:11
Оценка:
> ПК>Вне зависимости от порядка вызовов функции, ее аргументы могут вычисляться в разной последовательности, а — теоретически — даже и одновременно.
>
> Почему же теортетически: на архитектурах с явным параллелизмом на уровне инструкций (например Itanium) так и есть.

Теоретически потому что я не видел компилятора, использующего эту возможность.

> Собственно вопрос именно в этом и есть: где написано, что если есть хотя одна такая последовательность, то компилятор может выбрать именно ее.


Дело в обратном: нигде нет требования, чтобы он ее не выбирал.

> Я считаю, что он может все перемещать только в пределех между двумя точками следования, но ни в коем случае за их пределеами. Т.е. точки следования ставят барьеры в пределеах которых, можно тасовать вычисления как компилятор захочет, пока не нарушаются зависимости по данным.


Точки следования не определяют порядок вычисления, они только отделяют применение побочных эффектов одних вычислений от других. Когда компилятор выберет порядок вычислений, в дело вступят точки следования, предписывая компилятору места, не позднее которых побочные эффекты должны быть применены. Т.е. если есть выражение:
f(++i) + f(++i)

то стандарт не требует от компилятора вычислять аргументы вызовов непосредственно перед самими вызовами. Т.е. вполне допустимыми последовательностями вычислений, например, являются:
1) вычислить аргумент первого вызова, вызвать первую функцию, вычислить аргумент второго вызова, вызвать вторую функцию;
2) вычислить аргумент второго вызова, вызвать вторую функцию, вычислить аргумент первого вызова, вызвать первую функцию;
3) вычислить аргумент первого вызова, вычислить аргумент второго вызова, вызвать первую функцию, вызвать вторую функцию;
4) вычислить аргумент первого вызова, вычислить аргумент второго вызова, вызвать вторую функцию, вызвать первую функцию.

В последних двух случаях между вычислением аргументов точек следования нет.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[6]: sequence point (по мотивам инкремента)
От: Павел Кузнецов  
Дата: 04.06.04 09:19
Оценка:
> ПК>И второе тоже: побочные эффекты выражения не могут начаться раньше его вычисления
>
> А есть ли в Стандарте такое требование?

Это уже машина времени какая-то получается
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[10]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 09:30
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

>> ПК>Вне зависимости от порядка вызовов функции, ее аргументы могут вычисляться в разной последовательности, а — теоретически — даже и одновременно.

>>
>> Почему же теортетически: на архитектурах с явным параллелизмом на уровне инструкций (например Itanium) так и есть.

ПК>Теоретически потому что я не видел компилятора, использующего эту возможность.


Любой компилятор под Itanium — так устроен процессор, что они исполняет срзу несколько команд, так что ни одному компилятору это обойти (если он хочет порождать хоть чуть-чуть эффективный код). Например MSVC или Intel. Ну да это не важно.

>> Собственно вопрос именно в этом и есть: где написано, что если есть хотя одна такая последовательность, то компилятор может выбрать именно ее.


ПК>Дело в обратном: нигде нет требования, чтобы он ее не выбирал.


Это странное утверждение. Обощая можно получить, что компилятор вообще не должен генерировать работающую прогроамму Хотелось бы чего-то более конкретного.

ПК>В последних двух случаях между вычислением аргументов точек следования нет.


Какие могут быть варианты — понятно. Не понятно почему он может их свободно выбирать, если это нарушит работоспособность программы. Пример я уже приводил f(i++), f(i++) все должно вычисляться с лева-на-право (это не нарушено), но аргументы следую твоей логике получаем такую последовательность i++ i++ f() f(). Где фундаментальное отличие. То, что все реализаци понимают пример с запятой как надо — не доказательство.
Re[11]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 09:40
Оценка:
Здравствуйте, dupamid, Вы писали:

ПК>>Дело в обратном: нигде нет требования, чтобы он ее не выбирал.


D>Это странное утверждение. Обощая можно получить, что компилятор вообще не должен генерировать работающую прогроамму Хотелось бы чего-то более конкретного.


Так об этом и речь. Не должен. Если в программе UB, компилятор не должен генерировать работающую программу.

D>Пример я уже приводил f(i++), f(i++)


Стоп-стоп-стоп! Здесь все определено. Откуда взялась запятая? До сих пор ее нигде не было.
Re[12]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 09:46
Оценка:
Здравствуйте, Аноним, Вы писали:

D>>Это странное утверждение. Обощая можно получить, что компилятор вообще не должен генерировать работающую прогроамму Хотелось бы чего-то более конкретного.


А>Так об этом и речь. Не должен. Если в программе UB, компилятор не должен генерировать работающую программу.


Так следуя такой логике, что все что не запрещено разрешено — он ее вообще не обязан генерировать — даже если все определено

D>>Пример я уже приводил f(i++), f(i++)


А>Стоп-стоп-стоп! Здесь все определено. Откуда взялась запятая? До сих пор ее нигде не было.


Появилась Я обобщил логику выноса вычислений аргумента за перделы точки следования. Чтобы показать что если следовать такой логике, то не должны работать и более простые примеры.
Re[13]: sequence point (по мотивам инкремента)
От: Аноним  
Дата: 04.06.04 10:00
Оценка:
Здравствуйте, dupamid, Вы писали:

D>>>Это странное утверждение. Обощая можно получить, что компилятор вообще не должен генерировать работающую прогроамму Хотелось бы чего-то более конкретного.


А>>Так об этом и речь. Не должен. Если в программе UB, компилятор не должен генерировать работающую программу.


D>Так следуя такой логике, что все что не запрещено разрешено — он ее вообще не обязан генерировать — даже если все определено


Гм, наверное, давайте не будем цепляться к слову "генерировать", и сформулируем следующим образом.

Поведение абстрактной машины, исполняющей программу, должно быть таким, какое описывает стандарт. Если стандарт не дает в каком-либо случае явного описания требуемого поведения (пресловутое UB), то поведение абстракной машины в этом случае может быть любым ("не должен генерировать работающую программу").

D>>>Пример я уже приводил f(i++), f(i++)


А>>Стоп-стоп-стоп! Здесь все определено. Откуда взялась запятая? До сих пор ее нигде не было.


D>Появилась Я обобщил логику выноса вычислений аргумента за перделы точки следования. Чтобы показать что если следовать такой логике, то не должны работать и более простые примеры.


Не вижу я в письме Павла никакого "выноса вычислений аргумента за перделы точки следования". В обсуждавшемся примере нет точки следования между двумя подвыражениями, содержащими модификацию переменной. Итого — UB. В Вашем новом примере такая точка следования появилась (как мы выяснили, ее вносит оператор запятая). Well-defined behavior.
Re[14]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 10:07
Оценка:
Здравствуйте, Аноним, Вы писали:

D>>>>Пример я уже приводил f(i++), f(i++)


А>>>Стоп-стоп-стоп! Здесь все определено. Откуда взялась запятая? До сих пор ее нигде не было.


D>>Появилась Я обобщил логику выноса вычислений аргумента за перделы точки следования. Чтобы показать что если следовать такой логике, то не должны работать и более простые примеры.


А>Не вижу я в письме Павла никакого "выноса вычислений аргумента за перделы точки следования". В обсуждавшемся примере нет точки следования между двумя подвыражениями, содержащими модификацию переменной. Итого — UB. В Вашем новом примере такая точка следования появилась (как мы выяснили, ее вносит оператор запятая). Well-defined behavior.


Точка следования есть в каждом из аргументов, так что не важно в какой последовательности они вычисляются. Так что для того чтобы вычислить значения аргументов обеих функций перед вызовам, нужно перенести код вычисления одного из операндов через точку следования другого операнда (какой будет первый, какой второй не важно — один из них будет, а код второго придется переносить через точку следования первого).
Re[11]: sequence point (по мотивам инкремента)
От: Павел Кузнецов  
Дата: 04.06.04 10:13
Оценка:
>>> Собственно вопрос именно в этом и есть: где написано, что если есть хотя одна такая последовательность, то компилятор может выбрать именно ее.
>
> ПК>Дело в обратном: нигде нет требования, чтобы он ее не выбирал.
>
> Это странное утверждение. Обощая можно получить, что компилятор вообще не должен генерировать работающую прогроамму

В случае наличия undefined behavior — конечно. В противном случае (1.4/2):

If a program contains no violations of the rules in this International Standard, a conforming implementation shall, within its resource limits, accept and correctly execute3) that program.


> ПК>В последних двух случаях между вычислением аргументов точек следования нет.

>
> Какие могут быть варианты — понятно. Не понятно почему он может их свободно выбирать, если это нарушит работоспособность программы.

Работоспособность программы — абстрактное понятие. Стандарт же оперирует понятиями разряда "соответствие поведения программы поведению абстрактной машины". При этом, для описываемого случая поведение абстрактной машины не определено, соответсвенно о работоспособности говорить не приходится...

> Пример я уже приводил f(i++), f(i++) все должно вычисляться с лева-на-право (это не нарушено), но аргументы следую твоей логике получаем такую последовательность i++ i++ f() f(). Где фундаментальное отличие.


Фундаментальное отличие в том, что в случае наличия запятой, стандарт явно определяет порядок вычислений:

All side effects (1.9) of the left expression, except for the destruction of temporaries (12.2), are performed before the evaluation of the right expression.

В примере без запятой какого-либо определенного порядка нет.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[15]: sequence point (по мотивам инкремента)
От: Павел Кузнецов  
Дата: 04.06.04 10:34
Оценка:
> Точка следования есть в каждом из аргументов, так что не важно в какой последовательности они вычисляются. Так что для того чтобы вычислить значения аргументов обеих функций перед вызовам, нужно перенести код вычисления одного из операндов через точку следования другого операнда (какой будет первый, какой второй не важно — один из них будет, а код второго придется переносить через точку следования первого).

Нет точки следования в аргументах. Есть точки следования где-то между вычислением аргументов и перед вызовами функций. Кроме того, абсолютный порядок вычисления аргументов и вызовов функций стандарт не задает, более того, напротив, явно указывает на его произвольность:

Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.

Единственное, что стандарт определяет в этом отношении — относительный порядок вычисления аргументов функции и ее вызова. Т.е. аргументы будут вычислены до вызова функции, в которую они передаются. Будет это сделано до вызова других функций в том же выражении, или после — стандарт не определяет.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[12]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 04.06.04 10:46
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

>> Пример я уже приводил f(i++), f(i++) все должно вычисляться с лева-на-право (это не нарушено), но аргументы следую твоей логике получаем такую последовательность i++ i++ f() f(). Где фундаментальное отличие.


ПК>Фундаментальное отличие в том, что в случае наличия запятой, стандарт явно определяет порядок вычислений:

ПК>

All side effects (1.9) of the left expression, except for the destruction of temporaries (12.2), are performed before the evaluation of the right expression.

ПК>В примере без запятой какого-либо определенного порядка нет.

А порядок и не нарушается, в начале вычисляется левый операнд (только в него подмешивается правый операнд ) все его побочные действия завершаются, потом довычисляется правый, и все его побочные действия завершаются. Так что порядок не нарушился в соответствии с этим пунктом. Мы просто начали вычислять второй операнд до точки следования.
Re[7]: sequence point (по мотивам инкремента)
От: jazzer Россия Skype: enerjazzer
Дата: 04.06.04 11:26
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

>> ПК>И второе тоже: побочные эффекты выражения не могут начаться раньше его вычисления :-)

>>
>> А есть ли в Стандарте такое требование? ;)

ПК>Это уже машина времени какая-то получается :)


необязательно
это может быть второй процессор :)
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: sequence point (по мотивам инкремента)
От: Lorenzo_LAMAS  
Дата: 04.06.04 12:15
Оценка:
Здесь есть UB.
Of course, the code must be complete enough to compile and link.
Re[6]: sequence point (по мотивам инкремента)
От: Lorenzo_LAMAS  
Дата: 04.06.04 12:25
Оценка:
А>(Должен сказать, что в этом вопросе я несколько радикальнее Павла. По моему мнению, например, в следующем примере — UB:
А>
А>    i = (++i, 0);
А>

А>Павел, если я все правильно помню, с этим согласен не был.)

А не могли бы Вы объяснить в чем тут, по Вашему мнению, дело? Т.е. Вы считаете, что все-таки между двумя точками следования i модифицируется дважды благодаря ++ и присваиванию и несмотря на точку следования после ',' ?
Of course, the code must be complete enough to compile and link.
Re[8]: sequence point (по мотивам инкремента)
От: jazzer Россия Skype: enerjazzer
Дата: 04.06.04 14:36
Оценка:
Здравствуйте, Аноним, Вы писали:

Вы бы зарегистировались, что ли...
А то все Аноним до Аноним...
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[8]: sequence point (по мотивам инкремента)
От: Ivan A. Kosarev  
Дата: 04.06.04 17:17
Оценка:
wrote in message news:667226@news.rsdn.ru...

> P.S. В этом документе дается более подробное и лучше формализованное, чем в стандартах C/C++, описание точек следования. (В нем, в частности, рассматриваемый пример имеет well-defined behavior.) Но документ этот так и не стал нормативным ни для C, ни для C++.


Это более подробное и формализированное изложение того, что не может быть принято. Существуют практически важные примеры, не укладывающиеся в такое определение точек следования.

>
>     i = A;
>
где подвыражение A модифицирует переменную i. Точек следования в полном выражении нет. Переменная i модифицируется более одного раза между двумя соседними точками следования (1 и 3).


Есть хороший пример на эту тему:

i = f(&i);

Здесь точка следования есть, но сути это не меняет.

Еще вспоминаю давнюю дискуссию между Павлом Кузнецовым и dupamid. Павел говорил о неатомарности присваиваний. Так вот кроме того, что об атомарности (кроме sig_atomic_t и смежного с ним) в стандартах ничего не говорится, существует значительная масса кода в духе

link = link -> next;

которая никакой неатомарности не потерпит.

--
Иван

9899 — Полный справочник по языку Си
http://subscribe.ru/catalog/comp.soft.prog.9899
Posted via RSDN NNTP Server 1.9 alpha
Re[11]: sequence point (по мотивам инкремента)
От: Шахтер Интернет  
Дата: 05.06.04 00:20
Оценка:
Здравствуйте, dupamid, Вы писали:

D>Это странное утверждение. Обощая можно получить, что компилятор вообще не должен генерировать работающую прогроамму Хотелось бы чего-то более конкретного.


Вот конкретный пример. Можете его откомпилировать под Itanium.

int fun(int *i,int *j) { return ++(*i) + ++(*j); }


Данная функция нормально себя ведет, если i и j указывают на разные переменные и в этом случае параллелизация инкрементов -- хорошая идея.
Но вот если они указывают на одну переменную...

P.S. Пример несколько исскуственный.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[12]: sequence point (по мотивам инкремента)
От: folk Россия  
Дата: 05.06.04 02:19
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Здравствуйте, dupamid, Вы писали:


D>>Это странное утверждение. Обощая можно получить, что компилятор вообще не должен генерировать работающую прогроамму Хотелось бы чего-то более конкретного.


Ш>Вот конкретный пример. Можете его откомпилировать под Itanium.


Ш>
Ш>int fun(int *i,int *j) { return ++(*i) + ++(*j); }
Ш>


Ш>Данная функция нормально себя ведет, если i и j указывают на разные переменные и в этом случае параллелизация инкрементов -- хорошая идея.

Ш>Но вот если они указывают на одну переменную...

Ну так здесь неопределенное поведение в случае, когда i и j указывают на одну переменную. После асма dupamida такое ограничение мне начинает казаться разумным.

Кстати, в студенчестве мы проходили среди прочего конвейерные (команда может быть выбрана на исполнение до завершения исполнения предыдущей команды) и суперскалярные (несколько одновременно работающих конвейеров) архитектуры процессоров. Помню что некоторые процессоры с помощью какой-то магии умеют определять истинные зависимости по данным такого рода (чтение после записи) и в качестве контрмеры приостанавливают конвейер(ы), пока не будет завершено выполнение первой команды. Может Itanium тоже так умеет и в ситуации, когда i и j указывают на один объект, инкременты будут выполнены последовательно?

Ш>P.S. Пример несколько исскуственный.
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[13]: sequence point (по мотивам инкремента)
От: Шахтер Интернет  
Дата: 05.06.04 03:23
Оценка:
Здравствуйте, folk, Вы писали:

F>Здравствуйте, Шахтер, Вы писали:


Ш>>Здравствуйте, dupamid, Вы писали:


D>>>Это странное утверждение. Обощая можно получить, что компилятор вообще не должен генерировать работающую прогроамму Хотелось бы чего-то более конкретного.


Ш>>Вот конкретный пример. Можете его откомпилировать под Itanium.


Ш>>
Ш>>int fun(int *i,int *j) { return ++(*i) + ++(*j); }
Ш>>


Ш>>Данная функция нормально себя ведет, если i и j указывают на разные переменные и в этом случае параллелизация инкрементов -- хорошая идея.

Ш>>Но вот если они указывают на одну переменную...

F>Ну так здесь неопределенное поведение в случае, когда i и j указывают на одну переменную.


Ага. Вопрос только -- зачем компилятору генерировать "плохой" код.

F>После асма dupamida такое ограничение мне начинает казаться разумным.


F>Кстати, в студенчестве мы проходили среди прочего конвейерные (команда может быть выбрана на исполнение до завершения исполнения предыдущей команды) и суперскалярные (несколько одновременно работающих конвейеров) архитектуры процессоров.


F>Помню что некоторые процессоры с помощью какой-то магии умеют определять истинные зависимости по данным такого рода (чтение после записи) и в качестве контрмеры приостанавливают конвейер(ы), пока не будет завершено выполнение первой команды.


Не некоторые. Все современные процессоры -- конвейерные. И все они осуществляют приостановку конвейера при возникновении зависимости между входом и выходом.
Иначе такой процессор невозможно было бы использовать. Магии тут нет никакой -- просто схемотехника.

Если интересно и есть свободное время, можешь глянуть вот этот документ.
Особенно главу Pipeline.

F>Может Itanium тоже так умеет и в ситуации, когда i и j указывают на один объект, инкременты будут выполнены последовательно?


Зависит от того, что сгенерирует компилятор.

Пример (условный). В одной строчке -- параллельно выполняющиеся команды.

i = [SP+offset i]
j = [SP+offset j]

R1 <- i R2 <- j
R3 <- [R1] R4 <- [R2]
R3++ R4++
R0 <- R3 + R4 // возвращаем значение в R0
i <- R3 j <- R4

В последнеё строчке конфликт доступа к памяти. Проц как-то разрешит его, но это не сделает результат работы функции осмысленным.

Пусть i и j указывают на одну ячейку со значением 0. Тогда функция вернет 2. При последоватнльном же выполнении, т.е.

int i_res=++(*i); 
int j_res=++(*j);

return i_res+j_res; // Всё что мы сделали -- разбили выражение на два подвыражения.


функция вернула бы 3.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[13]: sequence point (по мотивам инкремента)
От: folk Россия  
Дата: 05.06.04 03:24
Оценка:
Здравствуйте, folk, Вы писали:

[]

F>Кстати, в студенчестве мы проходили среди прочего конвейерные (команда может быть выбрана на исполнение до завершения исполнения предыдущей команды) и суперскалярные (несколько одновременно работающих конвейеров) архитектуры процессоров. Помню что некоторые процессоры с помощью какой-то магии умеют определять истинные зависимости по данным такого рода (чтение после записи) и в качестве контрмеры приостанавливают конвейер(ы), пока не будет завершено выполнение первой команды. Может Itanium тоже так умеет и в ситуации, когда i и j указывают на один объект, инкременты будут выполнены последовательно?


Скорее всего я выдавал желаемое за действительное. Скорее всего могут отслеживаться зависимости по данным для регистров процессора, что особенно актуально при неупорядоченном выполнении команд. А в нашем случае связь между *i и *j разрывается в момент их загрузки в разные регистры. Чтобы разруливать подобные ситуации нужна очень сильная магия
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[14]: sequence point (по мотивам инкремента)
От: folk Россия  
Дата: 05.06.04 06:06
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>>>Вот конкретный пример. Можете его откомпилировать под Itanium.


Ш>>>
Ш>>>int fun(int *i,int *j) { return ++(*i) + ++(*j); }
Ш>>>


Ш>>>Данная функция нормально себя ведет, если i и j указывают на разные переменные и в этом случае параллелизация инкрементов -- хорошая идея.

Ш>>>Но вот если они указывают на одну переменную...

F>>Ну так здесь неопределенное поведение в случае, когда i и j указывают на одну переменную.


Ш>Ага. Вопрос только -- зачем компилятору генерировать "плохой" код.


Потому что "плохой" быстрее, ибо полнее использует параллелизацию операций.

F>>Кстати, в студенчестве мы проходили среди прочего конвейерные (команда может быть выбрана на исполнение до завершения исполнения предыдущей команды) и суперскалярные (несколько одновременно работающих конвейеров) архитектуры процессоров.


F>>Помню что некоторые процессоры с помощью какой-то магии умеют определять истинные зависимости по данным такого рода (чтение после записи) и в качестве контрмеры приостанавливают конвейер(ы), пока не будет завершено выполнение первой команды.


Ш>Не некоторые. Все современные процессоры -- конвейерные.


Ш>И все они осуществляют приостановку конвейера при возникновении зависимости между входом и выходом. Иначе такой процессор невозможно было бы использовать.


Ты уверен что это обязательное условие и что все современные процы это могут? Судя по приведенному dupaid'ом asm-листингу, точки, где нужно ожидать окончания выполнения предыдущих команд, определяются не процессором самостоятельно, а компилятором. Или я не так понял листинг?

Ш>Магии тут нет никакой -- просто схемотехника.


Схемотехника для меня — магия

Ш>Если интересно и есть свободное время, можешь глянуть вот этот документ.

Ш>Особенно главу Pipeline.

Никуда не спешу, так что попробую

F>>Может Itanium тоже так умеет и в ситуации, когда i и j указывают на один объект, инкременты будут выполнены последовательно?


Ш>Зависит от того, что сгенерирует компилятор.


Ш>Пример (условный). В одной строчке -- параллельно выполняющиеся команды.


Ш>i = [SP+offset i]

Ш>j = [SP+offset j]

Ш>R1 <- i R2 <- j

Ш>R3 <- [R1] R4 <- [R2]
Ш>R3++ R4++
Ш>R0 <- R3 + R4 // возвращаем значение в R0
Ш>i <- R3 j <- R4

Ш>В последнеё строчке конфликт доступа к памяти. Проц как-то разрешит его, но это не сделает результат работы функции осмысленным.


Ш>Пусть i и j указывают на одну ячейку со значением 0. Тогда функция вернет 2. При последоватнльном же выполнении, т.е.


Ш>
Ш>int i_res=++(*i); 
Ш>int j_res=++(*j);

Ш>return i_res+j_res; // Всё что мы сделали -- разбили выражение на два подвыражения.
Ш>


Ш>функция вернула бы 3.


Это понятно. Я имел ввиду, что может быть процу удастся последовательно выполнить инкременты даже для первого (параллельного) варианта. Но теперь думаю что это вряд ли.

Afaik, компилятор C++ вправе действовать по первому сценарию, т.е. генерировать более быстрый код. Перепишем второй вариант на псевдоасме, получится менее эффективно:
i = [SP+offset i]
j = [SP+offset j]

R1 <- i     R2 <- j
R3 <- [R1]    
R3++        
i <- R3
R4 <- [R2]
R4++
j <- R4
R0 <- R3 + R4 // возвращаем значение в R0
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[15]: sequence point (по мотивам инкремента)
От: folk Россия  
Дата: 05.06.04 07:10
Оценка:
Здравствуйте, folk, Вы писали:

Ш>>Если интересно и есть свободное время, можешь глянуть вот этот документ.

Ш>>Особенно главу Pipeline.

Прочитал Pipeline, поправь если чего не так понял.
Конфликты доступа к памяти там разрешаются с помощью приостановки конвейера. Но такой конфликт, насколько я понял, происходит при доступе не только к одному адресу, но вообще при доступе к одному блоку памяти, т.е. разрешается именно структурный конфликт, а не зависимость по данным. А в случае чтения после записи по одному и тому же адресу происходит чтение значения из внутреннего буфера — это уже оптимальное разрешение зависимости по данным.

Но этот DSP не является хорошим примером. Насколько я понял, адрес операнда является частью команды, что позволяет легко засекать возникновение зависимостей.
А в контексте этого разговора скорее интересны механизмы определения зависимостей, характерных для RISC-архитектур. Т.е. когда операции загрузки/выгрузки регистров в память и операции над данными в регистрах разделены. На вскидку — можно для каждого регистра хранить соответствующий адрес памяти (если такой есть) для определения зависимостей.
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[15]: sequence point (по мотивам инкремента)
От: Шахтер Интернет  
Дата: 05.06.04 23:13
Оценка:
Здравствуйте, folk, Вы писали:

Ш>>И все они осуществляют приостановку конвейера при возникновении зависимости между входом и выходом. Иначе такой процессор невозможно было бы использовать.


F>Ты уверен что это обязательное условие и что все современные процы это могут?


Уверен. Просто потому, что разрешить конфликт чтение-запись в большинстве случаев можно только уже в процессе исполнения. Ибо только в этот момент становятся известны адреса.
Альтернатива -- вставлять nop ы при первых подозрениях. Но в этом случае процессор будет работать фактически на сокращённой частоте -- а тогда и конвеер не нужен, теряется смысл всех нагромождений.

F>Судя по приведенному dupaid'ом asm-листингу, точки, где нужно ожидать окончания выполнения предыдущих команд, определяются не процессором самостоятельно, а компилятором. Или я не так понял листинг?


Не могу сказать, я с Itanium ом не знаком.

Ш>>Магии тут нет никакой -- просто схемотехника.


F>Схемотехника для меня — магия


Да брось -- нет там никакой магии. И вообще -- каждому программеру по осцилографу на стол!
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[16]: sequence point (по мотивам инкремента)
От: Шахтер Интернет  
Дата: 05.06.04 23:33
Оценка:
Здравствуйте, folk, Вы писали:

F>Здравствуйте, folk, Вы писали:


Ш>>>Если интересно и есть свободное время, можешь глянуть вот этот документ.

Ш>>>Особенно главу Pipeline.

F>Прочитал Pipeline, поправь если чего не так понял.

F>Конфликты доступа к памяти там разрешаются с помощью приостановки конвейера.

Да.

F> Но такой конфликт, насколько я понял, происходит при доступе не только к одному адресу, но вообще при доступе к одному блоку памяти, т.е. разрешается именно структурный конфликт, а не зависимость по данным.


Это и есть зависимость по данным. Данные лежать в банках памяти. Доступ к банку, вообще говоря, осуществляется в монопольном режиме.

F>А в случае чтения после записи по одному и тому же адресу происходит чтение значения из внутреннего буфера — это уже оптимальное разрешение зависимости по данным.


Нет. Происходит задержка конвейера.

For example:

STL A, *AR1+ ; AR1 and AR3 points at the same SARAM block.

LD *AR3, B ; This instruction takes 1 additional cycle due to a memory access conflict.

F>Но этот DSP не является хорошим примером.

F>Насколько я понял, адрес операнда является частью команды, что позволяет легко засекать возникновение зависимостей.

Неправильно понял. Пример выше.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[13]: sequence point (по мотивам инкремента)
От: Павел Кузнецов  
Дата: 06.06.04 14:54
Оценка:
>>> Пример я уже приводил f(i++), f(i++) все должно вычисляться с лева-на-право (это не нарушено), но аргументы следую твоей логике получаем такую последовательность i++ i++ f() f(). Где фундаментальное отличие.
>
> ПК>Фундаментальное отличие в том, что в случае наличия запятой, стандарт явно определяет порядок вычислений:
> ПК>

All side effects (1.9) of the left expression, except for the destruction of temporaries (12.2), are performed before the evaluation of the right expression.

> ПК>В примере без запятой какого-либо определенного порядка нет.
>
> А порядок и не нарушается, в начале вычисляется левый операнд (только в него подмешивается правый операнд ) все его побочные действия завершаются, потом довычисляется правый, и все его побочные действия завершаются. Так что порядок не нарушился в соответствии с этим пунктом. Мы просто начали вычислять второй операнд до точки следования.

Как видишь, в случае наличия запятой, это противоречит процитированной части стандарта, согласно которой, никакого подмешивания правого операнда в левый быть не может.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[12]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 07.06.04 08:44
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Вот конкретный пример. Можете его откомпилировать под Itanium.

Ш>
Ш>int fun(int *i,int *j) { return ++(*i) + ++(*j); }
Ш>


fun:
{   .mmi
    alloc    r10=ar.pfs,2,0,0,0
    ld4    r9=[r32]
    nop.i    0 ;;
}
{   .mmi
    add    r8=1,r9 ;;
    st4    [r32]=r8 // записываем
    nop.i    0
}
{   .mmi
    ld4    r3=[r33] ;; // и читаем в одной группе
    add    r2=1,r3
    nop.i    0 ;;
}
{   .mib
    st4    [r33]=r2
    add    r8=r8,r2
    br.ret.sptk.many    b0 ;;
}

В данном случае архитектура гарантирует правильность исполнения (мы прочитаем, то что запишем), но может быть потеря производительности.
Re[9]: sequence point (по мотивам инкремента)
От: dupamid Россия  
Дата: 07.06.04 09:20
Оценка:
Здравствуйте, Ivan A. Kosarev, Вы писали:

IAK>Еще вспоминаю давнюю дискуссию между Павлом Кузнецовым и dupamid. Павел говорил о неатомарности присваиваний. Так вот кроме того, что об атомарности (кроме sig_atomic_t и смежного с ним) в стандартах ничего не говорится, существует значительная масса кода в духе

IAK>link = link -> next;
IAK>которая никакой неатомарности не потерпит.

Производители процессоров всеми силами стремятся избежать неопределенности, или того что запись/чтение в одну ячейку могут вызвать серьезные проблемы. Так как существует огромное количество кода с такими "нарушениями". Поэтому, например, Itanium поддерживает явный параллелизм на уровне инструкций, но работа с памятью синхранизована на аппаратном уровне, чтобы не было таких проблем, так как софтверно их трудно разрешить. Инструкции работы с памятью выполняются в том порядке как записаны, если области перекрывающиеся — потеря производительности, но работает все как ожидалось.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.